/**********
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.
 */

#include "ngspice.h"

#ifdef HAVE_LIBGEN_H /* dirname */
#include <libgen.h>
#define HAVE_DECL_BASENAME 1
#endif

#include "cpdefs.h"
#include "ftedefs.h"
#include "fteext.h"
#include "dvec.h"
#include "fteinp.h"
#include "compatmode.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*/

#include "error.h" /* controlled_exit() */

/* 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];
static COMPATMODE_T inp_compat_mode;

/* 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 */
unsigned 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 */

extern COMPATMODE_T ngspice_compat_mode(void) ;

/* 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(void);
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(void);
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 );
static void inp_compat(struct line *deck);
static void inp_bsource_compat(struct line *deck);

/*-------------------------------------------------------------------------*
 *  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 = (char*) 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 = (char*) trealloc(strptr, memlen + 1))) {
            return (NULL);
         }
      }
      if (c == '\n') {
         break;
      }
   }
   if (!strlen) {
      tfree(strptr);
      return (NULL);
   }
//   strptr[strlen] = '\0'; 
   /* Trim the string */
   strptr = (char*) 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. 
  For MS Windows: First try the path of the source file.                                         
 *-------------------------------------------------------------------------*/
FILE *
inp_pathopen(char *name, char *mode)
{
    FILE *fp;
    char buf[BSIZE_SP];
    struct variable *v;

#if defined(HAS_WINDOWS)
    char buf2[BSIZE_SP];
    /* search in the path where the source (input) file has been found,
       but only if "name" is just a file name */
    if (!(index(name, DIR_TERM)) && cp_getvar("sourcefile", CP_STRING, buf2)) {
       /* If pathname is found, get path. 
       (char *dirname(const char *name) might have been used here) */
       if (substring(DIR_PATHSEP, buf2)) {
          int i,j=0;
          for (i=0; i<BSIZE_SP-1; i++) {
             if (buf2[i] == '\0') break;
             if (buf2[i] == DIR_TERM) j=i;
          }
          buf2[j+1] = '\0'; /* include path separator */
       }
       /* add file name */
       strcat(buf2, name);
       /* try to open file */
       if ((fp = fopen(buf2, mode)))
          return (fp);
    }
#endif

    /* If this is an abs pathname, or there is no sourcepath var, just
     * do an fopen.
     */
    if (index(name, DIR_TERM)
	    || !cp_getvar("sourcepath", CP_LIST, (char *) &v))
        return (fopen(name, mode));

    while (v) {
        switch (v->va_type) {
            case CP_STRING:
		cp_wstrip(v->va_string);
		(void) sprintf(buf, "%s%s%s", v->va_string, DIR_PATHSEP, name);
		break;
            case CP_NUM:
		(void) sprintf(buf, "%d%s%s", v->va_num, DIR_PATHSEP, name);
		break;
            case CP_REAL:   /* This is foolish */
		(void) sprintf(buf, "%g%s%s", v->va_real, DIR_PATHSEP, name);
		break;
            default:
            {
                fprintf(stderr, "ERROR: enumeration value `CP_BOOL' or `CP_LIST' not handled in inp_pathopen\nAborting...\n" );
                controlled_exit(EXIT_FAILURE);
            }
        }
        if ((fp = fopen(buf, mode)))
            return (fp);
        v = v->va_next;
    }
    return (NULL);
}

/* replace "gnd" by " 0 " 
   Delimiters of gnd may be ' ' or ',' or '(' or ')' */
static void
inp_fix_gnd_name( struct line *deck ) {
  struct line *c = deck;
  char *gnd;

  while ( c != NULL ) {
    gnd = c->li_line;
    // if there is a comment or no gnd, go to next line
    if (( *gnd == '*' ) || (strstr( gnd, "gnd" ) == NULL)) { c = c->li_next; continue; }
    // replace "§gnd§" by "§ 0 §", § being a ' '  ','  '('  ')'.
	while ( (gnd = strstr( gnd, "gnd" ) ) ) {
      if (( isspace(*(gnd-1)) || *(gnd-1) == '(' || *(gnd-1) == ','  ) &&
         ( isspace(*(gnd+3)) || *(gnd+3) == ')' || *(gnd+3) == ','  ))
      {
        memcpy( gnd, " 0 ", 3 );
      }
      gnd += 3;
    }
    // now remove the extra white spaces around 0
    c->li_line = inp_remove_ws(c->li_line); 
    c = c->li_next;
  }
}

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", CP_STRING, rawfile ) ) {
	line = (char*) 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", CP_STRING, rawfile ) ) {
      line = (char*) 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;
  bool        is_func = FALSE;

  for ( card = begin_card; card != NULL; card = card->li_next ) {

    if ( *card->li_line == '*' ) continue;

    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 = (char*) tmalloc( strlen(".subckt") + strlen(str_ptr) + 1 );
	sprintf( new_str, ".subckt%s", str_ptr );
      } else {
	new_str = (char*) 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 , int no_of_lines)
{
   struct line *card;
   char **used_subckt_names, **used_model_names, *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;
   int skip_control = 0;

   /* generate arrays of *char for subckt or model names. Start
   with 1000, but increase, if number of lines in deck is larger */
   if (no_of_lines < 1000) no_of_lines = 1000;
   used_subckt_names = (char**)tmalloc(no_of_lines*sizeof(char*));
   used_model_names = (char**)tmalloc(no_of_lines*sizeof(char*));

   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;

      /* there is no .subckt, .model or .param inside .control ... .endc */
      if ( ciprefix(".control", line) ) {
         skip_control ++;
         continue;
      } else if( ciprefix(".endc", line) ) {
         skip_control --;
         continue;
      } else if(skip_control > 0) {
         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]);
   tfree(used_subckt_names);
   tfree(used_model_names);
}



/* replace ternary operator ? : by fcn ternary_fcn() in .param, .func, and .meas lines */
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 ) {
      paren_ptr = NULL;
    }
  }
  else  return line; // hvogt

  // get conditional
  str_ptr2 = question = strstr( str_ptr, "?" );
  str_ptr2--;
  while ( isspace(*str_ptr2) ) str_ptr2--;
  if ( *str_ptr2 == ')' ) {
    count = 1;
    str_ptr = str_ptr2;
    while ( (count != 0) && (str_ptr != line) ){
      str_ptr--;
      if ( *str_ptr == '(' ) count--;
      if ( *str_ptr == ')' ) count++;
    }
  }
  str_ptr2++; keep = *str_ptr2; *str_ptr2 = '\0';
  conditional = strdup(str_ptr);
  *str_ptr2 = keep;

  // get beginning (whatever is left before the conditional)
  keep = *str_ptr; *str_ptr = '\0';
  beg_str = strdup(line);
  *str_ptr = 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);
      controlled_exit(EXIT_FAILURE);
    }
    colon = str_ptr2 + 1;
    while ( *colon != ':' && *colon != '\0' ) colon++;
    if ( *colon != ':' ) {
      fprintf(stderr,"ERROR: problem parsing ternary string (finding ':') %s!\n", line);
      controlled_exit(EXIT_FAILURE);
    }
    str_ptr2 = colon - 1;
    while ( isspace(*str_ptr2) ) str_ptr2--;	
  }
  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);
    controlled_exit(EXIT_FAILURE);
  }
  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 );
      controlled_exit(EXIT_FAILURE);
    }
    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 = (char*) 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 = (char*) 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 = (char*) 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 = (char*) 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;
   bool found_control = FALSE;

   for ( card = start_card; card != NULL; card = card->li_next ) {
      line = card->li_line;

      /* exclude replacement of ternary function between .control and .endc */
      if ( ciprefix( ".control", line ) ) found_control = TRUE;
      if ( ciprefix( ".endc",    line ) ) found_control = FALSE; 
      if (found_control) continue;

      /* ternary operator for B source done elsewhere */
      if ( *line == 'B' || *line == 'b' ) continue;
      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, bool comfile)
/* fp: in, pointer to file to be read, 
   data: out, linked list of cards
   call_depth: in, nested call to fcn
   dir_name: in, name of directory of file to be read
   comfile: in, TRUE if coammnd file (e.g. spinit, .spiceinit
*/
{
   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 *new_title = NULL;
   char 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;
   FILE *fdo;
   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;

   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 */
   for (;;) {
      /* 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 = (char*) 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 = (char *) tmalloc(strlen(ipc_buffer) + 3);
            strcpy(buffer, ipc_buffer);
            strcat(buffer, "\n");
         }
         else {  /* No good way to report this so just die */
            controlled_exit(EXIT_FAILURE);
         }
      }

/* 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 .title statement */
      if (ciprefix(".title", buffer)) {
         for ( s = buffer; *s && !isspace(*s); s++ ) /* skip over .title */
             ;
         while ( isspace(*s) ) s++;             /* advance past space chars */

         /* only the last title line remains valid */
          if (new_title != NULL) tfree(new_title);
          new_title = copy(s);
          *buffer = '*';             /* change .TITLE line to comment line */
      }

      /* 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), FALSE);
                  tfree(s_dup);
               }
               else
                  inp_readall(newfp, &libraries[num_libraries-1], call_depth+1, dir_name, FALSE);
            
               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() */
            controlled_exit(EXIT_FAILURE);
//            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 */
               }
               fprintf(cp_err,  "Error: .include statement failed.\n");
               tfree(buffer);		/* allocated by readline() above */
               controlled_exit(EXIT_FAILURE);
            }
         }
	    
         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), FALSE);  /* read stuff in include file into netlist */
            tfree(s_dup);
         }
         else
            inp_readall(newfp, &newcard, call_depth+1, dir_name, FALSE);  /* 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';
      }

/*      find the true .end command out of .endc, .ends, .endl, .end (comments may follow) */
      if (ciprefix(".end", buffer)) {
          if ((*(buffer+4) == '\0') || ( isspace(*(buffer+4))))  {
             found_end = TRUE;
             *buffer   = '*';
          }
      }

      if ( shell_eol_continuation ) {
         char *new_buffer = (char*) 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 = (char*) 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" );
                  controlled_exit(EXIT_FAILURE);
               }

               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 = (char*) 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++;
      }
   }

   /* Replace first line with the new title, if available */
   if (new_title != NULL) {
       if (cc->li_line) tfree(cc->li_line);
       cc->li_line = new_title;
   }

   /* 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 = (char*) 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;
      }
   }

   /* The following processing of an input file is not required for command files
      like spinit or .spiceinit, so return command files here. */ 
   if (comfile) {
      /* save the return value (via **data) */
      *data = cc;
      return;
   }

   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);

      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", CP_BOOL, NULL))
         inp_add_control_section(working, &line_number);
      inp_compat_mode = ngspice_compat_mode() ;
      if (inp_compat_mode == COMPATMODE_ALL) {
          /* Do all the compatibility stuff here */
          working = cc->li_next;
          /* E, G, L, R, C compatibility transformations */
          inp_compat(working);
          working = cc->li_next;
          /* B source numparam compatibility transformation */
          inp_bsource_compat(working);
      }
   }

   /* save the return value (via **data) */
   *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 (ft_ngdebug) {
   /*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);
   }
   /* 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;
}

/*-------------------------------------------------------------------------*
   removes  " " quotes, returns lower case letters, 
   replaces non-printable characterss with '_'                                                                       *
 *-------------------------------------------------------------------------*/
void
inp_casefix(char *string)
{
#ifdef HAVE_CTYPE_H
   if (string)
      while (*string) {
#ifdef HAS_ASCII
         /* ((*string) & 0177): mask off all but the first seven bits, 0177: octal */
         *string = strip(*string);
#endif
         if (*string == '"') {
            *string++ = ' ';
            while (*string && *string != '"')
               string++;
            if (*string== '\0') continue; /* needed if string is "something ! */
            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 */
    if(*s=='*') return;	  /* line is already a comment */    
    /* 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 ( *beg && (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;
      if( keep == '\0' ){
	/* End of string - don't go over end.  This needed to change because
	 * strings don't store line size here anymore since we use dynamic
	 * strings.  Previously, we could step on string size which was stored
	 * at string[length+2] and string[length+2] */
	beg = ptr2 ; 
      } else {
	*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 = (char*) 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 = (char*) 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 = (char*) 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 echo, let, set, 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 )) || (ciprefix( "echo", c->li_line ))
          || (ciprefix( "let", c->li_line )) || (ciprefix( "set", 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;
  bool found_control = FALSE;
  while ( c != NULL ) {
    if ( *c->li_line == '*' ) { c = c->li_next; continue; }
 
    /* exclude echo lines between .control and .endc from removing white spaces */
    if ( ciprefix( ".control", c->li_line ) ) found_control = TRUE;
    if ( ciprefix( ".endc",    c->li_line ) ) found_control = FALSE; 
    if ((found_control) && (ciprefix( "echo", 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(void)
{
  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 = (char*) 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 = (char*) 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 = (char*) 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 ) {
    /* no 'm' for B line or comment line */
    if ((*(card->li_line) == '*') || (*(card->li_line) == 'b')) 
        continue;
    new_str = (char*) 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 */
      if (param_ptr == search_ptr) /* no valid 'before' */
         before = '\0';
      else
         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 = (char*) 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 = (char*) 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 = (char*) tmalloc( strlen(curr_ptr) + strlen(params[i]) + 3 );
	  sprintf( new_str, "%s(%s)", curr_ptr, params[i] );
	} else {
	  new_str = (char*) 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 = (char*) 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;
      if ( open_paren_ptr != search_ptr) {
         while ( --fcn_name != search_ptr && (isalnum(*fcn_name) || *fcn_name == '_') )
             ;
         if ( !isalnum(*fcn_name) && *fcn_name != '_' ) fcn_name++;
      }

      *open_paren_ptr = '\0';
      close_paren_ptr = NULL;
      search_ptr = open_paren_ptr + 1;
      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 );
                  controlled_exit(EXIT_FAILURE);
               }
               *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 = ' ';
               }*/
               num_parens = 0;
               for (comma_ptr = curr_ptr; *comma_ptr && *comma_ptr != '\0'; comma_ptr++) {
                  if (*comma_ptr == ',' && num_parens == 0) {
                     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;
                  } 
                  if ( *comma_ptr == '(' ) num_parens++;
                  if ( *comma_ptr == ')' ) num_parens--;
               }
               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 );
                  controlled_exit(EXIT_FAILURE);
               }

               macro_str = inp_do_macro_param_replace( i, params );
               keep  = *fcn_name;
               *fcn_name = '\0';
               if ( curr_str == NULL ) {
                  new_str = (char*) 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 = (char*) 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 = '(';

   }

   if ( curr_str == NULL ) {
      curr_str = orig_ptr;
   }
   else {
      if ( str != NULL ) {
         new_str = (char*) 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(void)
{
  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;
  }
}

/* Put {} around tokens for handling in numparam.
   Searches for the next '=' in the line to become active.
   Several exceptions (eg. no 'set' or 'b' lines, no .cmodel lines, 
   no lines between .control and .endc, no .option lines).
   .cmodel instead of .model to allow skipping {} in model line, may be obsolete.
   Special handling of vectors with [] and complex values with < >
*/
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, "=" ) ) ) {

      // special case: .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN out_variable2=out_variable3
      // no braces around out_variable3. out_variable3 may be v(...) or i(...) 
      if ( ciprefix( ".meas", line ))
         if ((( *(equal_ptr+1) == 'v' ) || ( *(equal_ptr+1) == 'i' )) && ( *(equal_ptr+2) == '(' )) {
            // find closing ')' and skip token v(...) or i(...)
            while (( equal_ptr) && *equal_ptr != ')') equal_ptr++;
            line = equal_ptr + 1;
            continue;
         }
      
      // 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 = (char*) 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 = (char*) 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 = (char*) 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 = (char*) 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 = (char*) 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 = (char*) 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 = (char*) 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 = (char*) 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 = NULL, *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 );
      controlled_exit(EXIT_FAILURE);
    }
  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] );
          controlled_exit(EXIT_FAILURE);
 	}
      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, ioff = 1;
  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 ) ) )
          {  
            ioff = (strstr(param_ptr, "}") != NULL ? 1 : 0);  /* want prevent wrong memory access below */
            /* looking for curly braces or other string limiter */
            if ( ( !isalnum( *(param_ptr-ioff) )               && *(param_ptr-ioff) != '_'         &&
                   !isalnum( *(param_ptr+strlen(param_name)) ) && *(param_ptr+strlen(param_name)) != '_' )
                 || strcmp( param_ptr, param_name ) == 0) /* this are cases without curly braces */
              {
                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 = (char*) 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 = (char*) 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 );
      controlled_exit(EXIT_FAILURE);
    }

  /* 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 = (char*) 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 = (char*) 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   = (char*) 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;
}

/* pspice compatibility:
   ECOMP 3 0 TABLE {V(1,2)} = (-1MV 0V) (1MV, 10V)
   -->
   ECOMP 3 0 int3 int0 1
   BECOMP int3 int0 V = pwl(V(1,2), -1MV, 0 , 1MV, 10V)

   GD16 16 1 TABLE {V(16,1)} ((-100V,-1pV)(0,0)(1m,1u)(2m,1m))
   -->
   GD16 16 1 int_16 int_1 1
   BGD16 int_16 int_1 V = pwl (v(16,1) , -100V , -1pV , 0 , 0 , 1m , 1u , 2m , 1m )
*/

/* hspice compatibility:
   Exxx n1 n2 VCVS n3 n4 gain --> Exxx n1 n2 n3 n4 gain
   Gxxx n1 n2 VCCS n3 n4 tr --> Exxx n1 n2 n3 n4 tr

   Two step approach to keep the original names for reuse, 
   i.e. for current measurements like i(Exxx):
   Exxx n1 n2 VOL = {equation} 
   -->
   Exxx n1 n2 int1 0 1
   BExxx int1 0 V = {equation}

   Gxxx n1 n2 CUR = {equation} 
   --> 
   Gxxx n1 n2 int1 0 1
   BGxxx int1 0 V = {equation}

   Do the following transformations only if {equation} contains 
   simulation output like v(node), v(node1, node2), i(branch).
   Otherwise let do numparam the substitutions (R=const is handled
   in inp2r.c).
   
   Rxxx n1 n2 R = {equation} or Rxxx n1 n2 {equation}
   -->   
   BRxxx n1 n2 I = V(n1,n2)/{equation}
   
   Unfortunately the capability for ac noise calculation of 
   resistance may be lost.

   Cxxx n1 n2 C = {equation} or Cxxx n1 n2 {equation}
      -->
      Exxx  n-aux 0  n1 n2  1
      Cxxx  n-aux 0         1
      Bxxx  n2 n1  I = i(Exxx) * equation
   
   Lxxx n1 n2 L = {equation} or Lxxx n1 n2 {equation}
      -->
      Fxxx n-aux 0  Bxxx -1
      Lxxx n-aux 0      1
      Bxxx n1 n2 V = v(n-aux) * 1e-16

     */
static void inp_compat(struct line *deck)
{
    char *str_ptr, *cut_line, *title_tok, *node1, *node2;
    char *out_ptr, *exp_ptr, *beg_ptr, *end_ptr, *copy_ptr, *del_ptr;
    char *xline;
    size_t xlen, i, pai=0, paui=0, ii;
    char *ckt_array[100];
    struct line *new_line, *tmp_ptr;

    struct line  *param_end = NULL, *param_beg = NULL;
    struct line *card;
    int skip_control = 0;

    for (card = deck; card; card = card->li_next) {

        char *curr_line = card->li_line;

        /* exclude any command inside .control ... .endc */
        if ( ciprefix(".control", curr_line) ) {
            skip_control ++;
            continue;
        } else if( ciprefix(".endc", curr_line) ) {
            skip_control --;
            continue;
        } else if(skip_control > 0) {
            continue;
        }

        if ( *curr_line == '*' )
            continue;

        if ( *curr_line == 'e' ) {
            /*    Exxx n1 n2 VCVS n3 n4 gain --> Exxx n1 n2 n3 n4 gain
                  remove vcvs */
            if ((str_ptr = strstr( curr_line, "vcvs" )) != NULL) {
                *str_ptr = ' '; *(str_ptr + 1) = ' ';
                *(str_ptr + 2) = ' '; *(str_ptr + 3) = ' ';
            }

            /* Exxx n1 n2 VOL = {equation} 
               -->
               Exxx n1 n2 int1 0 1
               BExxx int1 0 V = {equation}
            */
            if ((str_ptr = strstr( curr_line, "vol" )) != NULL) {
                cut_line = curr_line;
                /* title and nodes */
                title_tok = gettok(&cut_line);
                node1 =  gettok(&cut_line);
                node2 =  gettok(&cut_line);
                /* Find equation, starts with '{', till end of line */
                str_ptr = strstr(cut_line, "{");

                // Exxx  n1 n2 int1 0 1
                xlen = 2*strlen(title_tok) + strlen(node1) + strlen(node2)
                    + 20 - 4*2 + 1;
                ckt_array[0] = (char*)tmalloc(xlen);
                sprintf(ckt_array[0], "%s %s %s %s_int1 0 1",
                    title_tok, node1, node2, title_tok);
                // BExxx int1 0 V = {equation} 
                xlen = 2*strlen(title_tok) + strlen(str_ptr)
                    + 20 - 3*2 + 1;
                ckt_array[1] = (char*)tmalloc(xlen);
                sprintf(ckt_array[1], "b%s %s_int1 0 v = %s",
                    title_tok, title_tok, str_ptr);

                // insert new B source line immediately after current line
	            tmp_ptr = card->li_next;
	            for ( i = 0; i < 2; 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    = ckt_array[i];
                    param_end->li_linenum = 0;
                }
                // comment out current variable e 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;
            }
        }
        else if ( *curr_line == 'g' ) {
            /* Gxxx n1 n2 VCCS n3 n4 tr --> Gxxx n1 n2 n3 n4 tr
               remove vccs */
            if ((str_ptr = strstr( curr_line, "vccs" )) != NULL) {
                *str_ptr = ' '; *(str_ptr + 1) = ' ';
                *(str_ptr + 2) = ' '; *(str_ptr + 3) = ' ';
            }

            /*
               Gxxx n1 n2 CUR = {equation} 
               --> 
               Gxxx n1 n2 int1 0 1
               BGxxx int1 0 V = {equation}
            */
            if ((str_ptr = strstr( curr_line, "cur" )) != NULL) {
                cut_line = curr_line;
                /* title and nodes */
                title_tok = gettok(&cut_line);
                node1 =  gettok(&cut_line);
                node2 =  gettok(&cut_line);
                /* Find equation, starts with '{', till end of line */
                str_ptr = strstr(cut_line, "{");

                // Gxxx  n1 n2 int1 0 1
                xlen = 2*strlen(title_tok) + strlen(node1) + strlen(node2)
                    + 20 - 4*2 + 1;
                ckt_array[0] = (char*)tmalloc(xlen);
                sprintf(ckt_array[0], "%s %s %s %s_int1 0 1",
                    title_tok, node1, node2, title_tok);
                // BGxxx int1 0 V = {equation} 
                xlen = 2*strlen(title_tok) + strlen(str_ptr)
                    + 20 - 3*2 + 1;
                ckt_array[1] = (char*)tmalloc(xlen);
                sprintf(ckt_array[1], "b%s %s_int1 0 v = %s",
                    title_tok, title_tok, str_ptr);

                // insert new B source line immediately after current line
	            tmp_ptr = card->li_next;
	            for ( i = 0; i < 2; 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    = ckt_array[i];
                    param_end->li_linenum = 0;
                }
                // comment out current variable g 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;
            }
        }
   /* Rxxx n1 n2 R = {equation} or Rxxx n1 n2 {equation}
      -->   
      BRxxx pos neg I = V(pos, neg)/{equation}
   */
        else if ( *curr_line == 'r' ) {
            if ((!strstr(curr_line, "v(")) &&  (!strstr(curr_line, "i(")))
                continue;
            cut_line = curr_line;
            /* make BRxxx pos neg I = V(pos, neg)/{equation}*/
            title_tok = gettok(&cut_line);
            node1 =  gettok(&cut_line);
            node2 =  gettok(&cut_line);
            /* Find equation, starts with '{', till end of line */
            str_ptr = strstr(cut_line, "{");
            xlen = strlen(title_tok) + strlen(node1) + strlen(node2) +
                   strlen(node1) + strlen(node2) + strlen(str_ptr)  +
                   28 - 6*2 + 1;
            xline = (char*)tmalloc(xlen);
            sprintf(xline, "b%s %s %s i = v(%s, %s)/(%s)", title_tok, node1, node2,  
                node1, node2, str_ptr);
            new_line = alloc(struct line);
	        new_line->li_next    = NULL;
	        new_line->li_error   = NULL;
	        new_line->li_actual  = NULL;
	        new_line->li_line    = xline;
	        new_line->li_linenum = 0;
	        // comment out current old R line
	        *(card->li_line)   = '*';
	        // insert new B source line immediately after current line
	        tmp_ptr           = card->li_next;
	        card->li_next     = new_line;
	        new_line->li_next = tmp_ptr;
	        // point 'card' pointer to the new line
	        card              = new_line;
        }
   /* Cxxx n1 n2 C = {equation} or Cxxx n1 n2 {equation}
      -->
      Exxx  n-aux 0  n1 n2  1
      Cxxx  n-aux 0         1
      Bxxx  n2 n1  I = i(Exxx) * equation
    */
        else if ( *curr_line == 'c' ) {
            if ((!strstr(curr_line, "v(")) &&  (!strstr(curr_line, "i(")))
                continue;
            cut_line = curr_line;
            /* title and nodes */
            title_tok = gettok(&cut_line);
            node1 =  gettok(&cut_line);
            node2 =  gettok(&cut_line);
            /* Find equation, starts with '{', till end of line */
            str_ptr = strstr(cut_line, "{");

            // Exxx  n-aux 0  n1 n2  1
            xlen = 2*strlen(title_tok) + strlen(node1) + strlen(node2)
                + 21 - 4*2 + 1;
            ckt_array[0] = (char*)tmalloc(xlen);
            sprintf(ckt_array[0], "e%s %s_int2 0 %s %s 1",
                    title_tok, title_tok, node1, node2);
            // Cxxx  n-aux 0  1
            xlen = 2*strlen(title_tok)
                + 15 - 2*2 + 1;
            ckt_array[1] = (char*)tmalloc(xlen);
            sprintf(ckt_array[1], "c%s %s_int2 0 1", title_tok, title_tok);
            // Bxxx  n2 n1  I = i(Exxx) * equation
            xlen = 2*strlen(title_tok) + strlen(node2) + strlen(node1)
                + strlen(str_ptr) + 27 - 2*5 + 1;
            ckt_array[2] = (char*)tmalloc(xlen);
            sprintf(ckt_array[2], "b%s %s %s i = i(e%s) * (%s)",
                    title_tok, node2, node1, title_tok, str_ptr);

            // insert new B source line immediately after current line
	        tmp_ptr = card->li_next;
	        for ( i = 0; i < 3; 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    = ckt_array[i];
                param_end->li_linenum = 0;
            }
            // comment out current variable capacitor 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;
        }

   /* Lxxx n1 n2 L = {equation} or Lxxx n1 n2 {equation}
      -->
      Fxxx n-aux 0  Bxxx -1
      Lxxx n-aux 0      1
      Bxxx n1 n2 V = v(n-aux) * equation
    */
        else if ( *curr_line == 'l' ) {
            if ((!strstr(curr_line, "v(")) &&  (!strstr(curr_line, "i(")))
                continue;
            cut_line = curr_line;
            /* title and nodes */
            title_tok = gettok(&cut_line);
            node1 =  gettok(&cut_line);
            node2 =  gettok(&cut_line);
            /* Find equation, starts with '{', till end of line */
            str_ptr = strstr(cut_line, "{");

            // Fxxx  n-aux 0  Bxxx  1
            xlen = 3*strlen(title_tok)
                + 20 - 3*2 + 1;
            ckt_array[0] = (char*)tmalloc(xlen);
            sprintf(ckt_array[0], "f%s %s_int2 0 b%s -1",
                    title_tok, title_tok, title_tok);
            // Lxxx  n-aux 0  1
            xlen = 2*strlen(title_tok)
                + 15 - 2*2 + 1;
            ckt_array[1] = (char*)tmalloc(xlen);
            sprintf(ckt_array[1], "l%s %s_int2 0 1", title_tok, title_tok);
            // Bxxx  n1 n2  V = v(n-aux) * equation
            xlen = 2*strlen(title_tok) + strlen(node2) + strlen(node1)
                + strlen(str_ptr) + 31 - 2*5 + 1;
            ckt_array[2] = (char*)tmalloc(xlen);
            sprintf(ckt_array[2], "b%s %s %s v = v(%s_int2) * (%s)",
                    title_tok, node1, node2, title_tok, str_ptr);

            // insert new B source line immediately after current line
	        tmp_ptr = card->li_next;
	        for ( i = 0; i < 3; 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    = ckt_array[i];
                param_end->li_linenum = 0;
            }
            // comment out current variable capacitor 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;
        }
     /* .probe -> .save
        .print, .plot, .save, .four, 
        An ouput vector may be replaced by the following:
        myoutput=par('expression')
        .meas
        A vector out_variable may be replaced by 
        par('expression')
     */
        else if ( *curr_line == '.' ) {
            // replace .probe by .save
            if((str_ptr = strstr(curr_line, ".probe")) != NULL)
                memcpy(str_ptr, ".save ", 6);

  /* Various formats for measure statement:
   * .MEASURE {DC|AC|TRAN} result WHEN out_variable=val
   * + <TD=td> <FROM=val> <TO=val>
   * + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
   *
   * .MEASURE {DC|AC|TRAN} result WHEN out_variable=out_variable2
   * + <TD=td> <FROM=val> <TO=val>
   * + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
   *
   * .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN out_variable2=val
   * + <TD=td> <FROM=val> <TO=val>
   * + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
   *
   * .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN out_variable2=out_variable3
   * + <TD=td>
   * + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
   *
   * .MEASURE {DC|AC|TRAN} result FIND out_variable AT=val
   * + <FROM=val> <TO=val>
   *
   * .MEASURE {DC|AC|TRAN} result {AVG|MIN|MAX|MIN_AT|MAX_AT|PP|RMS} out_variable
   * + <TD=td> <FROM=val> <TO=val>
   *
   * .MEASURE {DC|AC|TRAN} result INTEG<RAL> out_variable
   * + <TD=td> <FROM=val> <TO=val>
   *
   * .MEASURE {DC|AC|TRAN} result DERIV<ATIVE> out_variable AT=val
   *
   * .MEASURE {DC|AC|TRAN} result DERIV<ATIVE> out_variable WHEN out_variable2=val
   * + <TD=td>
   * + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
   *
   * .MEASURE {DC|AC|TRAN} result DERIV<ATIVE> out_variable WHEN out_variable2=out_variable3
   * + <TD=td>
   * + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>

   The user may set any out_variable to par(' expr ').
   We have to teplace this by v(pa_xx) and generate a B source line.

   * ----------------------------------------------------------------- */
            if ( ciprefix(".meas", curr_line) ) {
                if (strstr(curr_line, "par") == NULL) continue;
                 cut_line = curr_line;
                 // search for 'par'
                 while((str_ptr = strstr(cut_line, "par")) != NULL) {
                     if (pai > 99) {
                         fprintf(stderr, "ERROR: No more than 99 'par' per input file\n");
                         controlled_exit(EXIT_FAILURE);
                     }

                     // we have ' par({ ... })', the right delimeter is a ' ' or '='
                     if ( ciprefix(" par({", (str_ptr-1)) ) {
                         // find expression
                         beg_ptr = end_ptr = str_ptr + 5;
                         while ((*end_ptr != ' ') && (*end_ptr != '=') && (*end_ptr != '\0'))
                             end_ptr++;
                         exp_ptr = copy_substring(beg_ptr, end_ptr-2);
                         cut_line = str_ptr;
                         // generate node
                         out_ptr = (char*)tmalloc(6);
                         sprintf(out_ptr, "pa_%02d", pai);
                         // Bout_ptr  out_ptr 0  V = v(expr_ptr)
                         xlen = 2*strlen(out_ptr) + strlen(exp_ptr )+ 15 - 2*3 + 1;
                         ckt_array[pai] = (char*)tmalloc(xlen);
                         sprintf(ckt_array[pai], "b%s %s 0 v = %s",
                         out_ptr, out_ptr, exp_ptr);
                         ckt_array[++pai] = NULL;
                         // length of the replacement V(out_ptr)
                         xlen = strlen(out_ptr) + 4;
                         del_ptr = copy_ptr = (char*)tmalloc(xlen);
                         sprintf(copy_ptr, "v(%s)", out_ptr);
                         // length of the replacement part in original line
                         xlen = strlen(exp_ptr) + 7;
                         // copy the replacement without trailing '\0'
                         for (ii = 0; ii < xlen; ii++)
                             if (*copy_ptr)
                                *cut_line++ = *copy_ptr++;
                             else 
                                 *cut_line++ = ' ';

                         tfree(del_ptr); tfree(exp_ptr); tfree(out_ptr);
                     }
                     // or we have '={par({ ... })}', the right delimeter is a ' '
                     else if ( ciprefix("={par({", (str_ptr-2)) ) {
                         // find expression
                         beg_ptr = end_ptr = str_ptr + 5;
                         while ((*end_ptr != ' ') && (*end_ptr != '\0'))
                             end_ptr++;
                         exp_ptr = copy_substring(beg_ptr, end_ptr-3);
                         // generate node
                         out_ptr = (char*)tmalloc(6);
                         sprintf(out_ptr, "pa_%02d", pai);
                         // Bout_ptr  out_ptr 0  V = v(expr_ptr)
                         xlen = 2*strlen(out_ptr) + strlen(exp_ptr )+ 15 - 2*3 + 1;
                         ckt_array[pai] = (char*)tmalloc(xlen);
                         sprintf(ckt_array[pai], "b%s %s 0 v = %s",
                         out_ptr, out_ptr, exp_ptr);
                         ckt_array[++pai] = NULL;
                         // length of the replacement V(out_ptr)
                         xlen = strlen(out_ptr) + 4;
                         del_ptr = copy_ptr = (char*)tmalloc(xlen);
                         sprintf(copy_ptr, "v(%s)", out_ptr);
                         // length of the replacement part in original line
                         xlen = strlen(exp_ptr) + 9;
                         // skip '='
                         cut_line++;
                         // copy the replacement without trailing '\0'
                         for (ii = 0; ii < xlen; ii++)
                             if (*copy_ptr)
                                *cut_line++ = *copy_ptr++;
                             else *cut_line++ = ' ';

                         tfree(del_ptr); tfree(exp_ptr); tfree(out_ptr);
                     }
                     // nothing to replace
                     else {
                         cut_line = str_ptr + 1;
                         continue;
                     }

                 } // while 'par'
                 // no replacement done, go to next line
                 if (pai == paui) continue;
                 // remove white spaces
                 card->li_line = inp_remove_ws(curr_line);
                 // insert new B source line immediately after current line
	             tmp_ptr = card->li_next;
	             for ( ii = paui; ii < pai; ii++ )
                 {
                     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    = ckt_array[ii];
                     param_end->li_linenum = 0;
                }

                // 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;
                paui = pai;
            }
            else if (( ciprefix(".save", curr_line) ) || ( ciprefix(".four", curr_line) )
             || ( ciprefix(".print", curr_line) ) || ( ciprefix(".plot", curr_line) )) {
                if (strstr(curr_line, "par") == NULL) continue;
                 cut_line = curr_line;
                 // search for 'par'
                 while((str_ptr = strstr(cut_line, "par")) != NULL) {
                     if (pai > 99) {
                         fprintf(stderr, "ERROR: No more than 99 'par' per input file!\n");
                         controlled_exit(EXIT_FAILURE);
                     }

                     // we have ' par({ ... })'
                      if ( ciprefix(" par({", (str_ptr-1)) ) {

                         // find expression
                         beg_ptr = end_ptr = str_ptr + 5;
                         while ((*end_ptr != ' ') && (*end_ptr != '\0'))
                             end_ptr++;
                         exp_ptr = copy_substring(beg_ptr, end_ptr-2);
                         cut_line = str_ptr;
                         // generate node
                         out_ptr = (char*)tmalloc(6);
                         sprintf(out_ptr, "pa_%02d", pai);
                         // Bout_ptr  out_ptr 0  V = v(expr_ptr)
                         xlen = 2*strlen(out_ptr) + strlen(exp_ptr )+ 15 - 2*3 + 1;
                         ckt_array[pai] = (char*)tmalloc(xlen);
                         sprintf(ckt_array[pai], "b%s %s 0 v = %s",
                         out_ptr, out_ptr, exp_ptr);
                         ckt_array[++pai] = NULL;
                         // length of the replacement V(out_ptr)
                         xlen = strlen(out_ptr) + 1;
                         del_ptr = copy_ptr = (char*)tmalloc(xlen);
                         sprintf(copy_ptr, "%s", out_ptr);
                         // length of the replacement part in original line
                         xlen = strlen(exp_ptr) + 7;
                         // copy the replacement without trailing '\0'
                         for (ii = 0; ii < xlen; ii++)
                             if (*copy_ptr)
                                *cut_line++ = *copy_ptr++;
                             else 
                                 *cut_line++ = ' ';

                         tfree(del_ptr); tfree(exp_ptr); tfree(out_ptr);
                     }
                     // or we have '={par({ ... })}'
                     else if ( ciprefix("={par({", (str_ptr-2)) ) {

                         // find myoutput
                         beg_ptr = end_ptr = str_ptr - 2;
                         while (*beg_ptr != ' ')
                             beg_ptr--;
                         out_ptr = copy_substring(beg_ptr + 1, end_ptr);
                         cut_line = beg_ptr + 1;
                         // find expression
                         beg_ptr = end_ptr = str_ptr + 5;
                         while ((*end_ptr != ' ') && (*end_ptr != '\0'))
                             end_ptr++;
                         exp_ptr = copy_substring(beg_ptr, end_ptr-3);
                         // Bout_ptr  out_ptr 0  V = v(expr_ptr)
                         xlen = 2*strlen(out_ptr) + strlen(exp_ptr )+ 15 - 2*3 + 1;
                         ckt_array[pai] = (char*)tmalloc(xlen);
                         sprintf(ckt_array[pai], "b%s %s 0 v = %s",
                         out_ptr, out_ptr, exp_ptr);
                         ckt_array[++pai] = NULL;
                         // length of the replacement V(out_ptr)
                         xlen = strlen(out_ptr) + 1;
                         del_ptr = copy_ptr = (char*)tmalloc(xlen);
                         sprintf(copy_ptr, "%s", out_ptr);
                         // length of the replacement part in original line
                         xlen = strlen(out_ptr) + strlen(exp_ptr) + 10;
                         // copy the replacement without trailing '\0'
                         for (ii = 0; ii < xlen; ii++)
                             if (*copy_ptr)
                                *cut_line++ = *copy_ptr++;
                             else *cut_line++ = ' ';

                         tfree(del_ptr); tfree(exp_ptr); tfree(out_ptr);
                     }
                     // nothing to replace
                     else
                         cut_line = str_ptr + 1;
                 } // while 'par'
                 // no replacement done, go to next line
                 if (pai == paui) continue;
                 // remove white spaces
                 card->li_line = inp_remove_ws(curr_line);
                 // insert new B source line immediately after current line
	             tmp_ptr = card->li_next;
	             for ( ii = paui; ii < pai; ii++ )
                 {
                     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    = ckt_array[ii];
                     param_end->li_linenum = 0;
                }
                // comment out current variable capacitor line
//                *(ckt_array[0])   = '*';
                // 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;
                paui = pai;
 //                continue;
            } // if .print etc.
        } // if('.')
    }
}
/* lines for B sources: no parsing in numparam code, just replacement of parameters.
   Parsing done in B source parser.
   To achive this, do the following:
   Remove all '{' and '}' --> no parsing of equations in numparam
   Place '{' and '}' directly around all potential parameters, 
   thus skip function names like exp (search for exp( to detect fcn name), 
   functions containing nodes like v(node), v(node1, node2), i(branch)
   and other keywords. --> Only parameter replacement in numparam

*/

static void inp_bsource_compat(struct line *deck)
{
    char *equal_ptr, *str_ptr, *tmp_char, *new_str, *final_str;
    char actchar, prevchar = ' ';
    struct line *card, *new_line, *tmp_ptr;
    wordlist *wl = NULL, *wlist = NULL, *cwl;
    char buf[512];
    size_t i, xlen, ustate = 0;
    int skip_control = 0;
    char *cvalue;
    double dvalue;
    int error1;

    for (card = deck; card; card = card->li_next) {

        char *curr_line = card->li_line;

        /* exclude any command inside .control ... .endc */
        if ( ciprefix(".control", curr_line) ) {
            skip_control ++;
            continue;
        } else if( ciprefix(".endc", curr_line) ) {
            skip_control --;
            continue;
        } else if(skip_control > 0) {
            continue;
        }

        if ( *curr_line == 'b' ) {
            /* remove white spaces of everything inside {}*/
            card->li_line = inp_remove_ws(card->li_line);
            curr_line = card->li_line;
            /* store starting point for later parsing, beginning of {expression} */
            equal_ptr = strstr(curr_line, "=");
            /* find the m={m} token and remove it */
            if((str_ptr = strstr(curr_line, "m={m}")) != NULL)
                memcpy( str_ptr, "     ", 5 ); 
            /* scan the line and remove all '{' and '}' */
            str_ptr = curr_line;
            while (*str_ptr) {
                if ((*str_ptr == '{') || (*str_ptr == '}'))
                    *str_ptr = ' ';
                str_ptr++;
            }
            /* scan the expression */
            str_ptr = equal_ptr + 1;
            while (*str_ptr != '\0') {
                while ((*str_ptr != '\0') && isspace(*str_ptr))
                    str_ptr++;
                if (*str_ptr == '\0') break;
                actchar = *str_ptr;
                cwl = alloc(struct wordlist);
                cwl->wl_prev = wl;
                if (wl)
                    wl->wl_next = cwl;
                else {
                    wlist = cwl;
                    cwl->wl_next = NULL;
                }
                if ((actchar == ',') || (actchar == '(') || (actchar == ')')
                    || (actchar == '*') || (actchar == '/') || (actchar == '^')
                    || (actchar == '+') || (actchar == '?') || (actchar == ':'))
                {                    
                    if ((actchar == '*') && (*(str_ptr+1) == '*')) {
                        actchar = '^';
                        str_ptr++;
                    }
                    buf[0] = actchar;
                    buf[1] = '\0';
                    cwl->wl_word = copy(buf);
                    str_ptr++;
                    if (actchar == ')') ustate = 0;
                    else ustate = 1; /* we have an operator */
                }
                else if ((actchar == '>') || (actchar == '<')
                    || (actchar == '!') || (actchar == '=') )
                {                    
                    /* >=, <=, !=, == */
                    if ((*(str_ptr+1) == '=')) {
                        buf[0] = actchar;
                        buf[1] = '=';
                        buf[2] = '\0';
                        str_ptr++;
                    }
                    else {
                        buf[0] = actchar;
                        buf[1] = '\0';
                    }
                    cwl->wl_word = copy(buf);
                    str_ptr++;
                    ustate = 1; /* we have an operator */
                }
                else if ((actchar == '-') && (ustate == 0)) {
                    buf[0] = actchar;
                    buf[1] = '\0';
                    cwl->wl_word = copy(buf);
                    str_ptr++;
                    ustate = 1; /* we have an operator */
                }
                else if ((actchar == '-') && (ustate == 1)) {
                    cwl->wl_word = copy("");
                    str_ptr++;
                    ustate = 2; /* place a '-' in front of token */
                }
                else if (isalpha(actchar))
                {
                    /* unary -, change sign */
                    if (ustate == 2) {
                        i = 1;
                        buf[0] = '-';
                    }
                    else i = 0;

                    if (((actchar == 'v') || (actchar == 'i')) && (*(str_ptr+1) == '(')) {
                        while (*str_ptr != ')') {
                            buf[i] = *str_ptr;
                            i++;
                            str_ptr++;
                        }
                        buf[i] = *str_ptr;
                        buf[i+1] = '\0'; 
                        cwl->wl_word = copy(buf);
                        str_ptr++;
                    }
                    else {
                        while (isalnum(*str_ptr) || (*str_ptr == '!') || (*str_ptr == '#') 
                            || (*str_ptr == '$')|| (*str_ptr == '%')|| (*str_ptr == '_') 
                            || (*str_ptr == '[')|| (*str_ptr == ']')) {
                            buf[i] = *str_ptr;
                            i++;
                            str_ptr++;
                        }
                        buf[i] = '\0'; 
                        /* no parens {} around time, hertz, temper, the constants
                           pi and e which are defined in inpptree.c and around pwl */
                        if ((*str_ptr == '(') || cieq(buf, "hertz")  || cieq(buf, "temper") 
                            || cieq(buf, "time") || cieq(buf, "pi") || cieq(buf, "e")
                            || cieq(buf, "pwl"))
                        {
                            /* special handling of pwl lines:
                               Put braces around tokens and around expressions, use ','
                               as separator like:
                               pwl(i(Vin), {x0-1},{y0}, 
                                  {x0},{y0},{x1},{y1}, {x2},{y2},{x3},{y3}, 
                                  {x3+1},{y3})
                            */
/*
                            if (cieq(buf, "pwl")) {
                                // go past i(Vin)
                                i = 3;
                                while (*str_ptr != ')') {
                                    buf[i] = *str_ptr;
                                    i++;
                                    str_ptr++;
                                }
                                buf[i] = *str_ptr;
                                i++;
                                str_ptr++;
                                // find first ',' 
                                while (*str_ptr != ',') {
                                    buf[i] = *str_ptr;
                                    i++;
                                    str_ptr++;
                                }
                                buf[i] = *str_ptr;
                                i++;
                                buf[i] = '{';
                                i++;
                                str_ptr++;
                                while (*str_ptr != ')') {
                                    if (*str_ptr == ',') {
                                        buf[i] = '}';
                                        i++;
                                        buf[i] = ',';
                                        i++;
                                        buf[i] = '{';
                                        i++;
                                        str_ptr++;
                                    }
                                    else {
                                        buf[i] = *str_ptr;
                                        i++;
                                        str_ptr++;
                                    }
                                }
                                buf[i] = '}';
                                i++;
                                buf[i] = *str_ptr;
                                i++;
                                buf[i] = '\0';
                                str_ptr++;
                            }
*/
                            cwl->wl_word = copy(buf);
                        }
                        /* {} around all other tokens */
                        else {
                            xlen = strlen(buf);
                            tmp_char = (char*) tmalloc(xlen + 3);
                            sprintf(tmp_char, "{%s}", buf);
                            cwl->wl_word = tmp_char;
                        }
                    }
                    ustate = 0; /* we have a number */
                }
                else if (isdigit(actchar))
                {
                    /* allow 100p, 5MEG etc. */
                    dvalue = INPevaluate(&str_ptr, &error1, 2);
                    cvalue = (char*) tmalloc(19);
                    sprintf(cvalue,"%18.10e", dvalue);
                    /* unary -, change sign */
                    if (ustate == 2) {
                        cvalue[0] = '-';
                    }
                    cwl->wl_word = copy(cvalue);
                    ustate = 0; /* we have a number */
                }
                else  /* strange char */
                {
                    printf("Preparing B line for numparam\nWhat is this?\n%s\n", str_ptr);
                    buf[0] = *str_ptr;
                    buf[1] = '\0';
                    cwl->wl_word = copy(buf);
                    str_ptr++;
                }
                wl = cwl;
                prevchar = actchar;
            }

            new_str = wl_flatten(wlist);
            wl_free(wlist);
            wlist = NULL;
            wl = NULL;

	        tmp_char = copy(curr_line);
            equal_ptr = strstr(tmp_char, "=");
            /* cut the tmp_char after the equal sign */
            *(equal_ptr + 1) = '\0';  
            xlen = strlen(tmp_char) + strlen(new_str) + 2;
            final_str = (char*) tmalloc(xlen);
            sprintf(final_str, "%s %s", tmp_char, new_str);

            new_line = alloc(struct line);
	        new_line->li_next    = NULL;
	        new_line->li_error   = NULL;
	        new_line->li_actual  = NULL;
	        new_line->li_line    = final_str;
	        new_line->li_linenum = 0;
	        // comment out current old B line
	        *(card->li_line)   = '*';
	        // insert new B source line immediately after current line
	        tmp_ptr           = card->li_next;
	        card->li_next     = new_line;
	        new_line->li_next = tmp_ptr;
	        // point 'card' pointer to the new line
	        card              = new_line;

            tfree(new_str);
            tfree(tmp_char);
        } /* end of if 'b' */
    } /* end of for loop */
}
