/* 
 * Copyright (c) Tony Bybell 1999-2005.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */


/*
 * vcd.c 			23jan99ajb
 * evcd parts 			29jun99ajb
 * profiler optimizations 	15jul99ajb
 * more profiler optimizations	25jan00ajb
 * finsim parameter fix		26jan00ajb
 * vector rechaining code	03apr00ajb
 * multiple var section code	06apr00ajb
 * fix for duplicate nets	19dec00ajb
 * support for alt hier seps	23dec00ajb
 * fix for rcs identifiers	16jan01ajb
 * coredump fix for bad VCD	04apr02ajb
 * min/maxid speedup            27feb03ajb
 * bugfix on min/maxid speedup  06jul03ajb
 */
#include "vcd.h"

#undef VCD_BSEARCH_IS_PERFECT		/* bsearch is imperfect under linux, but OK under AIX */

int vcd_warning_filesize = -1;		/* -1 means use VCD_SIZE_WARN, 0 means disable, any other size is a compare in MB */
char autocoalesce=1, autocoalesce_reversal=0;

int vcd_explicit_zero_subscripts=-1;  /* 0=yes, -1=no */
char convert_to_reals=0;
char atomic_vectors=1;
char make_vcd_save_file=0;
char vcd_preserve_glitches=0;
FILE *vcd_save_handle=NULL;

static FILE *vcd_handle=NULL;
static char vcd_is_compressed=0;

static void add_histent(TimeType time, struct Node *n, char ch, int regadd, char *vector);
static void add_tail_histents(void);
static void vcd_build_symbols(void);
static void vcd_cleanup(void);
static void evcd_strcpy(char *dst, char *src);

static off_t vcdbyteno=0;
static int error_count=0;	/* should always be zero */

static int header_over=0;
static int dumping_off=0;
static TimeType start_time=-1;
static TimeType end_time=-1;
static TimeType current_time=-1;

static int num_glitches=0;
static int num_glitch_regions=0;

static char vcd_hier_delimeter[2]={0, 0};   /* fill in after rc reading code */

static struct vcdsymbol *pv=NULL, *rootv=NULL;
static char *vcdbuf=NULL, *vst=NULL, *vend=NULL;

/******************************************************************/

void strcpy_vcdalt(char *too, char *from, char delim)
{
char ch;

do
	{
	ch=*(from++);
	if(ch==delim)
		{
		ch=hier_delimeter;
		}
	} while((*(too++)=ch));
}

/******************************************************************/

static struct slist *slistroot=NULL, *slistcurr=NULL;
static char *slisthier=NULL;
static int slisthier_len=0;

/******************************************************************/

enum Tokens   { T_VAR, T_END, T_SCOPE, T_UPSCOPE,
		T_COMMENT, T_DATE, T_DUMPALL, T_DUMPOFF, T_DUMPON,
		T_DUMPVARS, T_ENDDEFINITIONS, 
		T_DUMPPORTS, T_DUMPPORTSOFF, T_DUMPPORTSON, T_DUMPPORTSALL,
		T_TIMESCALE, T_VERSION, T_VCDCLOSE,
		T_EOF, T_STRING, T_UNKNOWN_KEY };

char *tokens[]={ "var", "end", "scope", "upscope",
		 "comment", "date", "dumpall", "dumpoff", "dumpon",
		 "dumpvars", "enddefinitions",
		 "dumpports", "dumpportsoff", "dumpportson", "dumpportsall",
		 "timescale", "version", "vcdclose",
		 "", "", "" };

#define NUM_TOKENS 18

static int T_MAX_STR=1024;	/* was originally a const..now it reallocs */
static char *yytext=NULL;
static int yylen=0, yylen_cache=0;

#define T_GET tok=get_token();if((tok==T_END)||(tok==T_EOF))break;

/******************************************************************/

static struct vcdsymbol *vcdsymroot=NULL, *vcdsymcurr=NULL;
static struct vcdsymbol **sorted=NULL;
static struct vcdsymbol **indexed=NULL;

enum VarTypes { V_EVENT, V_PARAMETER,
		V_INTEGER, V_REAL, V_REG, V_SUPPLY0,
		V_SUPPLY1, V_TIME, V_TRI, V_TRIAND, V_TRIOR,
		V_TRIREG, V_TRI0, V_TRI1, V_WAND, V_WIRE, V_WOR, V_PORT,
		V_END, V_LB, V_COLON, V_RB, V_STRING };

char *vartypes[]={ "event", "parameter",
		"integer", "real", "reg", "supply0",
		"supply1", "time", "tri", "triand", "trior",
		"trireg", "tri0", "tri1", "wand", "wire", "wor", "port",
		"$end", "", "", "", ""};

#define NUM_VTOKENS 19

static int numsyms=0;

/******************************************************************/

/*
 * histent structs are NEVER freed so this is OK..
 */
#define VCD_HISTENT_GRANULARITY 100
static struct HistEnt *he_curr=NULL, *he_fini=NULL;

struct HistEnt *histent_calloc(void)
{
if(he_curr==he_fini)
	{
	he_curr=(struct HistEnt *)calloc_2(VCD_HISTENT_GRANULARITY, sizeof(struct HistEnt));
	he_fini=he_curr+VCD_HISTENT_GRANULARITY;
	}

return(he_curr++);	
}

/******************************************************************/

static struct queuedevent *queuedevents=NULL;

/******************************************************************/
 
static unsigned int vcd_minid = ~0;
static unsigned int vcd_maxid = 0;

static unsigned int vcdid_hash(char *s, int len)
{  
unsigned int val=0;
int i;

s+=(len-1);
                 
for(i=0;i<len;i++)
        {
        val *= 95;				/* was 94 but XL uses '!' as right hand side chars which act as leading zeros */
        val += (((unsigned char)*s) - 32);	/* was 33 but XL ... */
        s--;
        }

return(val);
}

/******************************************************************/

/*
 * bsearch compare
 */
static int vcdsymbsearchcompare(const void *s1, const void *s2)
{
char *v1;
struct vcdsymbol *v2;

v1=(char *)s1;
v2=*((struct vcdsymbol **)s2);

return(strcmp(v1, v2->id));
}


/*
 * actual bsearch
 */
static struct vcdsymbol *bsearch_vcd(char *key, int len)
{
struct vcdsymbol **v;
struct vcdsymbol *t;

if(indexed)
        {
        unsigned int hsh = vcdid_hash(key, len);
        if((hsh>=vcd_minid)&&(hsh<=vcd_maxid))
                {
                return(indexed[hsh-vcd_minid]);
                }

	return(NULL);
        }

if(sorted)
	{
	v=(struct vcdsymbol **)bsearch(key, sorted, numsyms, 
		sizeof(struct vcdsymbol *), vcdsymbsearchcompare);

	if(v)
		{
		#ifndef VCD_BSEARCH_IS_PERFECT
			for(;;)
				{
				t=*v;
		
				if((v==sorted)||(strcmp((*(--v))->id, key)))
					{
					return(t);
					}
				}
		#else
			return(*v);
		#endif
		}
		else
		{
		return(NULL);
		}
	}
	else
	{
	static int err = 0;
	if(!err)
		{
		fprintf(stderr, "Near byte %d, VCD search table NULL..is this a VCD file?\n", (int)(vcdbyteno+(vst-vcdbuf)));
		err=1;
		}
	return(NULL);
	}
}


/*
 * sort on vcdsymbol pointers
 */
static int vcdsymcompare(const void *s1, const void *s2)
{
struct vcdsymbol *v1, *v2;

v1=*((struct vcdsymbol **)s1);
v2=*((struct vcdsymbol **)s2);

return(strcmp(v1->id, v2->id));
}


/*
 * create sorted (by id) table
 */
static void create_sorted_table(void)
{
struct vcdsymbol *v;
struct vcdsymbol **pnt;
unsigned int vcd_distance;

if(sorted) 
	{
	free_2(sorted);	/* this means we saw a 2nd enddefinition chunk! */
	sorted=NULL;
	}

if(indexed)
	{
	free_2(indexed);
	indexed=NULL;
	}

if(numsyms)
	{
        vcd_distance = vcd_maxid - vcd_minid + 1;

        if(vcd_distance <= VCD_INDEXSIZ)
                {
                indexed = (struct vcdsymbol **)calloc_2(vcd_distance, sizeof(struct vcdsymbol *));
         
		/* printf("%d symbols span ID range of %d, using indexing...\n", numsyms, vcd_distance); */

                v=vcdsymroot;
                while(v)
                        {
                        if(!indexed[v->nid - vcd_minid]) indexed[v->nid - vcd_minid] = v;
                        v=v->next;
                        }
                }
                else
		{	
		pnt=sorted=(struct vcdsymbol **)calloc_2(numsyms, sizeof(struct vcdsymbol *));
		v=vcdsymroot;
		while(v)
			{
			*(pnt++)=v;
			v=v->next;
			}
	
		qsort(sorted, numsyms, sizeof(struct vcdsymbol *), vcdsymcompare);
		}
	}
}

/******************************************************************/

/*
 * single char get inlined/optimized
 */
static void getch_alloc(void)
{
vend=vst=vcdbuf=(char *)calloc_2(1,VCD_BSIZ);
}

static void getch_free(void)
{
free_2(vcdbuf);
vcdbuf=vst=vend=NULL;
}


static off_t vcd_fsiz = 0;

static int getch_fetch(void)
{
size_t rd;

errno = 0;
if(feof(vcd_handle)) return(-1);

vcdbyteno+=(vend-vcdbuf);
rd=fread(vcdbuf, sizeof(char), VCD_BSIZ, vcd_handle);
vend=(vst=vcdbuf)+rd;

if(vcd_fsiz)
	{
	splash_sync(vcdbyteno, vcd_fsiz);
	}

if((!rd)||(errno)) return(-1);

return((int)(*(vst++)));
}

#define getch() ((vst!=vend)?((int)(*(vst++))):(getch_fetch()))


static char *varsplit=NULL, *vsplitcurr=NULL;
static int getch_patched(void)
{
char ch;

ch=*vsplitcurr;
if(!ch)
	{
	return(-1);
	}
	else
	{
	vsplitcurr++;
	return((int)ch);
	}
}

/*
 * simple tokenizer
 */
static int get_token(void)
{
int ch;
int i, len=0;
int is_string=0;
char *yyshadow;

for(;;)
	{
	ch=getch();
	if(ch<0) return(T_EOF);
	if(ch<=' ') continue;	/* val<=' ' is a quick whitespace check      */
	break;			/* (take advantage of fact that vcd is text) */
	}
if(ch=='$') 
	{
	yytext[len++]=ch;
	for(;;)
		{
		ch=getch();
		if(ch<0) return(T_EOF);
		if(ch<=' ') continue;
		break;
		}
	}
	else
	{
	is_string=1;
	}

for(yytext[len++]=ch;;yytext[len++]=ch)
	{
	if(len==T_MAX_STR)
		{
		yytext=(char *)realloc_2(yytext, (T_MAX_STR=T_MAX_STR*2)+1);
		}
	ch=getch();
	if(ch<=' ') break;
	}
yytext[len]=0;	/* terminator */

if(is_string) 
	{
	yylen=len;
	return(T_STRING);
	}

yyshadow=yytext;
do
{
yyshadow++;
for(i=0;i<NUM_TOKENS;i++)
	{
	if(!strcmp(yyshadow,tokens[i]))
		{
		return(i);
		}
	}

} while(*yyshadow=='$'); /* fix for RCS ids in version strings */

return(T_UNKNOWN_KEY);
}


static int var_prevch=0;
static int get_vartoken_patched(void)
{
int ch;
int i, len=0;

if(!var_prevch)
	{
	for(;;)
		{
		ch=getch_patched();
		if(ch<0) { free_2(varsplit); varsplit=NULL; return(V_END); }
		if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')) continue;
		break;
		}
	}
	else
	{
	ch=var_prevch;
	var_prevch=0;
	}
	
if(ch=='[') return(V_LB);
if(ch==':') return(V_COLON);
if(ch==']') return(V_RB);

for(yytext[len++]=ch;;yytext[len++]=ch)
	{
	if(len==T_MAX_STR)
		{
		yytext=(char *)realloc_2(yytext, (T_MAX_STR=T_MAX_STR*2)+1);
		}
	ch=getch_patched();
	if(ch<0) break;
	if((ch==':')||(ch==']'))
		{
		var_prevch=ch;
		break;
		}
	}
yytext[len]=0;	/* terminator */

for(i=0;i<NUM_VTOKENS;i++)
	{
	if(!strcmp(yytext,vartypes[i]))
		{
		if(ch<0) { free_2(varsplit); varsplit=NULL; }
		return(i);
		}
	}

yylen=len;
if(ch<0) { free_2(varsplit); varsplit=NULL; }
return(V_STRING);
}

static int get_vartoken(void)
{
int ch;
int i, len=0;

if(varsplit)
	{
	int rc=get_vartoken_patched();
	if(rc!=V_END) return(rc);
	var_prevch=0;
	}

if(!var_prevch)
	{
	for(;;)
		{
		ch=getch();
		if(ch<0) return(V_END);
		if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')) continue;
		break;
		}
	}
	else
	{
	ch=var_prevch;
	var_prevch=0;
	}
	
if(ch=='[') return(V_LB);
if(ch==':') return(V_COLON);
if(ch==']') return(V_RB);

for(yytext[len++]=ch;;yytext[len++]=ch)
	{
	if(len==T_MAX_STR)
		{
		yytext=(char *)realloc_2(yytext, (T_MAX_STR=T_MAX_STR*2)+1);
		}
	ch=getch();
	if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')||(ch<0)) break;
	if((ch=='[')&&(yytext[0]!='\\'))
		{
		varsplit=yytext+len;		/* keep looping so we get the *last* one */
		}
	else
	if(((ch==':')||(ch==']'))&&(!varsplit)&&(yytext[0]!='\\'))
		{
		var_prevch=ch;
		break;
		}
	}
yytext[len]=0;	/* absolute terminator */
if((varsplit)&&(yytext[len-1]==']'))
	{
	char *vst;
	vst=malloc_2(strlen(varsplit)+1);
	strcpy(vst, varsplit);

	*varsplit=0x00;		/* zero out var name at the left bracket */
	len=varsplit-yytext;

	varsplit=vsplitcurr=vst;
	var_prevch=0;
	}
	else
	{
	varsplit=NULL;
	}

for(i=0;i<NUM_VTOKENS;i++)
	{
	if(!strcmp(yytext,vartypes[i]))
		{
		return(i);
		}
	}

yylen=len;
return(V_STRING);
}

static int get_strtoken(void)
{
int ch;
int len=0;

if(!var_prevch)
      {
      for(;;)
              {
              ch=getch();
              if(ch<0) return(V_END);
              if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')) continue;
              break;
              }
      }
      else
      {
      ch=var_prevch;
      var_prevch=0;
      }
      
for(yytext[len++]=ch;;yytext[len++]=ch)
      {
	if(len==T_MAX_STR)
		{
		yytext=(char *)realloc_2(yytext, (T_MAX_STR=T_MAX_STR*2)+1);
		}
      ch=getch();
      if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')||(ch<0)) break;
      }
yytext[len]=0;        /* terminator */

yylen=len;
return(V_STRING);
}

static void sync_end(char *hdr)
{
int tok;

if(hdr) DEBUG(fprintf(stderr,"%s",hdr));
for(;;)
	{
	tok=get_token();
	if((tok==T_END)||(tok==T_EOF)) break;
	if(hdr)DEBUG(fprintf(stderr," %s",yytext));
	}
if(hdr) DEBUG(fprintf(stderr,"\n"));
}

static char *build_slisthier(void)
{
struct slist *s;
int len=0;

if(!slistroot)
	{
	if(slisthier)
		{
		free_2(slisthier);
		}

	slisthier_len=0;
	slisthier=(char *)malloc_2(1);
	*slisthier=0;
	return(slisthier);
	}

s=slistroot; len=0;
while(s)
	{
	len+=s->len+(s->next?1:0);
	s=s->next;
	}

slisthier=(char *)malloc_2((slisthier_len=len)+1);
s=slistroot; len=0;
while(s)
	{
	strcpy(slisthier+len,s->str);
	len+=s->len;
	if(s->next)
		{
		strcpy(slisthier+len,vcd_hier_delimeter);
		len++;
		}
	s=s->next;
	}
return(slisthier);
}


void append_vcd_slisthier(char *str)
{
struct slist *s;
s=(struct slist *)calloc_2(1,sizeof(struct slist));
s->len=strlen(str);
s->str=(char *)malloc_2(s->len+1);
strcpy(s->str,str);

if(slistcurr)
	{
	slistcurr->next=s;
	slistcurr=s;
	}
	else
	{
	slistcurr=slistroot=s;
	}

build_slisthier();
DEBUG(fprintf(stderr, "SCOPE: %s\n",slisthier));
}


static void parse_valuechange(void)
{
struct vcdsymbol *v;
char *vector;
int vlen;

switch(yytext[0])
	{
	case '0':
	case '1':
	case 'x': case 'X':
	case 'z': case 'Z':
	case 'h': case 'H':
	case 'u': case 'U':
	case 'w': case 'W':
	case 'l': case 'L':
	case '-':
		if(yylen>1)
			{
			v=bsearch_vcd(yytext+1, yylen-1);	
			if(!v)
				{
				fprintf(stderr,"Near byte %d, Unknown VCD identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)),yytext+1);
				}
				else
				{
				if(v->vartype!=V_EVENT)
					{
					v->value[0]=yytext[0];
					DEBUG(fprintf(stderr,"%s = '%c'\n",v->name,v->value[0]));
					add_histent(current_time,v->narray[0],v->value[0],1, NULL);
					}
					else
					{
					v->value[0]=(dumping_off)?'x':'1'; /* only '1' is relevant */
					if(current_time!=(v->ev->last_event_time+1))
						{
						/* dump degating event */
						DEBUG(fprintf(stderr,"#"TTFormat" %s = '%c' (event)\n",v->ev->last_event_time+1,v->name,'0'));
						add_histent(v->ev->last_event_time+1,v->narray[0],'0',1, NULL);
						}
					DEBUG(fprintf(stderr,"%s = '%c' (event)\n",v->name,v->value[0]));
					add_histent(current_time,v->narray[0],v->value[0],1, NULL);
					v->ev->last_event_time=current_time;
					}
				}
			}
			else
			{
			fprintf(stderr,"Near byte %d, Malformed VCD identifier\n", (int)(vcdbyteno+(vst-vcdbuf)));
			}
		break;

	case 'b':
	case 'B':
		{
		/* extract binary number then.. */
		vector=malloc_2(yylen_cache=yylen); 
		strcpy(vector,yytext+1);
		vlen=yylen-1;

		get_strtoken();
		v=bsearch_vcd(yytext, yylen);	
		if(!v)
			{
			fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)), yytext);
			free_2(vector);
			}
			else
			{
			if ((v->vartype==V_REAL)||((convert_to_reals)&&((v->vartype==V_INTEGER)||(v->vartype==V_PARAMETER))))
				{
				double *d;
				char *pnt;
				char ch;
				TimeType k=0;
		
				pnt=vector;
				while((ch=*(pnt++))) { k=(k<<1)|((ch=='1')?1:0); }
				free_2(vector);
			
				d=malloc_2(sizeof(double));
				*d=(double)k;
			
				if(!v)
					{
					fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)), yytext);
					free_2(d);
					}
					else
					{
					add_histent(current_time, v->narray[0],'g',1,(char *)d);
					}
				break;
				}

			if(vlen<v->size) 	/* fill in left part */
				{
				char extend;
				int i, fill;

				extend=(vector[0]=='1')?'0':vector[0];

				fill=v->size-vlen;				
				for(i=0;i<fill;i++)
					{
					v->value[i]=extend;
					}
				strcpy(v->value+fill,vector);
				}
			else if(vlen==v->size) 	/* straight copy */
				{
				strcpy(v->value,vector);
				}
			else			/* too big, so copy only right half */
				{
				int skip;

				skip=vlen-v->size;
				strcpy(v->value,vector+skip);
				}
			DEBUG(fprintf(stderr,"%s = '%s'\n",v->name, v->value));

			if((v->size==1)||(!atomic_vectors))
				{
				int i;
				for(i=0;i<v->size;i++)
					{
					add_histent(current_time, v->narray[i],v->value[i],1, NULL);
					}
				free_2(vector);
				}
				else
				{
				if(yylen_cache!=(v->size+1))
					{
					free_2(vector);
					vector=malloc_2(v->size+1);
					}
				strcpy(vector,v->value);
				add_histent(current_time, v->narray[0],0,1,vector);
				}

			}
		break;
		}

	case 'p':
		/* extract port dump value.. */
		vector=malloc_2(yylen_cache=yylen); 
		strcpy(vector,yytext+1);
		vlen=yylen-1;

		get_strtoken();	/* throw away 0_strength_component */
		get_strtoken(); /* throw away 0_strength_component */
		get_strtoken(); /* this is the id                  */
		v=bsearch_vcd(yytext, yylen);	
		if(!v)
			{
			fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)), yytext);
			free_2(vector);
			}
			else
			{
			if ((v->vartype==V_REAL)||((convert_to_reals)&&((v->vartype==V_INTEGER)||(v->vartype==V_PARAMETER))))
				{
				double *d;
				char *pnt;
				char ch;
				TimeType k=0;
		
				pnt=vector;
				while((ch=*(pnt++))) { k=(k<<1)|((ch=='1')?1:0); }
				free_2(vector);
			
				d=malloc_2(sizeof(double));
				*d=(double)k;
			
				if(!v)
					{
					fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)), yytext);
					free_2(d);
					}
					else
					{
					add_histent(current_time, v->narray[0],'g',1,(char *)d);
					}
				break;
				}

			if(vlen<v->size) 	/* fill in left part */
				{
				char extend;
				int i, fill;

				extend='0';

				fill=v->size-vlen;				
				for(i=0;i<fill;i++)
					{
					v->value[i]=extend;
					}
				evcd_strcpy(v->value+fill,vector);
				}
			else if(vlen==v->size) 	/* straight copy */
				{
				evcd_strcpy(v->value,vector);
				}
			else			/* too big, so copy only right half */
				{
				int skip;

				skip=vlen-v->size;
				evcd_strcpy(v->value,vector+skip);
				}
			DEBUG(fprintf(stderr,"%s = '%s'\n",v->name, v->value));

			if((v->size==1)||(!atomic_vectors))
				{
				int i;
				for(i=0;i<v->size;i++)
					{
					add_histent(current_time, v->narray[i],v->value[i],1, NULL);
					}
				free_2(vector);
				}
				else
				{
				if(yylen_cache<v->size)
					{
					free_2(vector);
					vector=malloc_2(v->size+1);
					}
				strcpy(vector,v->value);
				add_histent(current_time, v->narray[0],0,1,vector);
				}
			}
		break;


	case 'r':
	case 'R':
		{
		double *d;

		d=malloc_2(sizeof(double));
		sscanf(yytext+1,"%lg",d);
		
		get_strtoken();
		v=bsearch_vcd(yytext, yylen);	
		if(!v)
			{
			fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)), yytext);
			free_2(d);
			}
			else
			{
			add_histent(current_time, v->narray[0],'g',1,(char *)d);
			}

		break;
		}

#ifndef STRICT_VCD_ONLY
	case 's':
	case 'S':
		{
		char *d;

		d=(char *)malloc_2(yylen);
		strcpy(d, yytext+1);
		
		get_strtoken();
		v=bsearch_vcd(yytext, yylen);	
		if(!v)
			{
			fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",(int)(vcdbyteno+(vst-vcdbuf)), yytext);
			free_2(d);
			}
			else
			{
			add_histent(current_time, v->narray[0],'s',1,(char *)d);
			}

		break;
		}
#endif
	}

}


static void evcd_strcpy(char *dst, char *src)
{
static char *evcd="DUNZduLHXTlh01?FAaBbCcf";
static char  *vcd="01xz0101xz0101xzxxxxxxx";

char ch;
int i;

while((ch=*src))
	{
	for(i=0;i<23;i++)
		{
		if(evcd[i]==ch)
			{
			*dst=vcd[i];
			break;
			}
		}	
	if(i==23) *dst='x';

	src++;
	dst++;
	}

*dst=0;	/* null terminate destination */
}


static void vcd_parse(void)
{
int tok;

for(;;)
	{
	switch(tok=get_token())
		{
		case T_COMMENT:
			sync_end("COMMENT:");
			break;
		case T_DATE:
			sync_end("DATE:");
			break;
		case T_VERSION:
			sync_end("VERSION:");
			break;
		case T_TIMESCALE:
			{
			int vtok;
			int i;
			char prefix=' ';

			vtok=get_token();
			if((vtok==T_END)||(vtok==T_EOF)) break;
			time_scale=atoi_64(yytext);
			if(!time_scale) time_scale=1;
			for(i=0;i<yylen;i++)
				{
				if((yytext[i]<'0')||(yytext[i]>'9'))
					{
					prefix=yytext[i];
					break;
					}
				}
			if(prefix==' ')
				{
				vtok=get_token();
				if((vtok==T_END)||(vtok==T_EOF)) break;
				prefix=yytext[0];		
				}
			switch(prefix)
				{
				case ' ':
				case 'm':
				case 'u':
				case 'n':
				case 'p':
				case 'f':
					time_dimension=prefix;
					break;
				case 's':
					time_dimension=' ';
					break;
				default:	/* unknown */
					time_dimension='n';
					break;
				}

			DEBUG(fprintf(stderr,"TIMESCALE: "TTFormat" %cs\n",time_scale, time_dimension));
			sync_end(NULL);
			}
			break;
		case T_SCOPE:
			T_GET;
			T_GET;
			if(tok==T_STRING)
				{
				struct slist *s;
				s=(struct slist *)calloc_2(1,sizeof(struct slist));
				s->len=yylen;
				s->str=(char *)malloc_2(yylen+1);
				strcpy(s->str,yytext);

				if(slistcurr)
					{
					slistcurr->next=s;
					slistcurr=s;
					}
					else
					{
					slistcurr=slistroot=s;
					}

				build_slisthier();
				DEBUG(fprintf(stderr, "SCOPE: %s\n",slisthier));
				}
			sync_end(NULL);
			break;
		case T_UPSCOPE:
			if(slistroot)
				{
				struct slist *s;

				s=slistroot;
				if(!s->next)
					{
					free_2(s->str);
					free_2(s);
					slistroot=slistcurr=NULL;
					}
				else
				for(;;)
					{
					if(!s->next->next)
						{
						free_2(s->next->str);
						free_2(s->next);
						s->next=NULL;
						slistcurr=s;
						break;
						}
					s=s->next;
					}
				build_slisthier();
				DEBUG(fprintf(stderr, "SCOPE: %s\n",slisthier));
				}
			sync_end(NULL);
			break;
		case T_VAR:
			if((header_over)&&(0))
			{
			fprintf(stderr,"$VAR encountered after $ENDDEFINITIONS near byte %d.  VCD is malformed, exiting.\n",
				(int)(vcdbyteno+(vst-vcdbuf)));
			exit(0);
			}
			else
			{
			int vtok;
			struct vcdsymbol *v=NULL;

			var_prevch=0;
			varsplit=NULL;
			vtok=get_vartoken();
			if(vtok>V_PORT) goto bail;

			v=(struct vcdsymbol *)calloc_2(1,sizeof(struct vcdsymbol));
			v->vartype=vtok;
			v->msi=v->lsi=vcd_explicit_zero_subscripts; /* indicate [un]subscripted status */

			if(vtok==V_PORT)
				{
				vtok=get_vartoken();
				if(vtok==V_STRING)
					{
					v->size=atoi_64(yytext);
					if(!v->size) v->size=1;
					}
					else 
					if(vtok==V_LB)
					{
					vtok=get_vartoken();
					if(vtok==V_END) goto err;
					if(vtok!=V_STRING) goto err;
					v->msi=atoi_64(yytext);
					vtok=get_vartoken();
					if(vtok==V_RB)
						{
						v->lsi=v->msi;
						v->size=1;
						}
						else
						{
						if(vtok!=V_COLON) goto err;
						vtok=get_vartoken();
						if(vtok!=V_STRING) goto err;
						v->lsi=atoi_64(yytext);
						vtok=get_vartoken();
						if(vtok!=V_RB) goto err;

						if(v->msi>v->lsi)
							{
							v->size=v->msi-v->lsi+1;
							}
							else
							{
							v->size=v->lsi-v->msi+1;
							}
						}
					}
					else goto err;

				vtok=get_strtoken();
				if(vtok==V_END) goto err;
				v->id=(char *)malloc_2(yylen+1);
				strcpy(v->id, yytext);
                                v->nid=vcdid_hash(yytext,yylen);

                                if(v->nid < vcd_minid) vcd_minid = v->nid;
                                if(v->nid > vcd_maxid) vcd_maxid = v->nid;

				vtok=get_vartoken();
				if(vtok!=V_STRING) goto err;
				if(slisthier_len)
					{
					v->name=(char *)malloc_2(slisthier_len+1+yylen+1);
					strcpy(v->name,slisthier);
					strcpy(v->name+slisthier_len,vcd_hier_delimeter);
					if(alt_hier_delimeter)
						{
						strcpy_vcdalt(v->name+slisthier_len+1,yytext,alt_hier_delimeter);
						}
						else
						{
						strcpy(v->name+slisthier_len+1,yytext);
						}
					}
					else
					{
					v->name=(char *)malloc_2(yylen+1);
					if(alt_hier_delimeter)
						{
						strcpy_vcdalt(v->name,yytext,alt_hier_delimeter);
						}
						else
						{
						strcpy(v->name,yytext);
						}
					}

                                if(pv)
                                        { 
                                        if(!strcmp(pv->name,v->name))
                                                {
                                                pv->chain=v;
                                                v->root=rootv;
                                                if(pv==rootv) pv->root=rootv;
                                                }
                                                else
                                                {
                                                rootv=v;
                                                }
                                        }
					else
					{
					rootv=v;
					}
                                pv=v;
				}
				else	/* regular vcd var, not an evcd port var */
				{
				vtok=get_vartoken();
				if(vtok==V_END) goto err;
				v->size=atoi_64(yytext);
				if(!v->size) v->size=1;
				vtok=get_strtoken();
				if(vtok==V_END) goto err;
				v->id=(char *)malloc_2(yylen+1);
				strcpy(v->id, yytext);
                                v->nid=vcdid_hash(yytext,yylen);
                                
                                if(v->nid < vcd_minid) vcd_minid = v->nid;
                                if(v->nid > vcd_maxid) vcd_maxid = v->nid;

				vtok=get_vartoken();
				if(vtok!=V_STRING) goto err;
				if(slisthier_len)
					{
					v->name=(char *)malloc_2(slisthier_len+1+yylen+1);
					strcpy(v->name,slisthier);
					strcpy(v->name+slisthier_len,vcd_hier_delimeter);
					if(alt_hier_delimeter)
						{
						strcpy_vcdalt(v->name+slisthier_len+1,yytext,alt_hier_delimeter);
						}
						else
						{
						strcpy(v->name+slisthier_len+1,yytext);
						}
					}
					else
					{
					v->name=(char *)malloc_2(yylen+1);
					if(alt_hier_delimeter)
						{
						strcpy_vcdalt(v->name,yytext,alt_hier_delimeter);
						}
						else
						{
						strcpy(v->name,yytext);
						}
					}

                                if(pv)
                                        { 
                                        if(!strcmp(pv->name,v->name))
                                                {
                                                pv->chain=v;
                                                v->root=rootv;
                                                if(pv==rootv) pv->root=rootv;
                                                }
                                                else
                                                {
                                                rootv=v;
                                                }
                                        }
					else
					{
					rootv=v;
					}
                                pv=v;
				
				vtok=get_vartoken();
				if(vtok==V_END) goto dumpv;
				if(vtok!=V_LB) goto err;
				vtok=get_vartoken();
				if(vtok!=V_STRING) goto err;
				v->msi=atoi_64(yytext);
				vtok=get_vartoken();
				if(vtok==V_RB)
					{
					v->lsi=v->msi;
					goto dumpv;
					}
				if(vtok!=V_COLON) goto err;
				vtok=get_vartoken();
				if(vtok!=V_STRING) goto err;
				v->lsi=atoi_64(yytext);
				vtok=get_vartoken();
				if(vtok!=V_RB) goto err;
				}

			dumpv:
			if((v->vartype==V_REAL)||((convert_to_reals)&&((v->vartype==V_INTEGER)||(v->vartype==V_PARAMETER))))
				{
				v->vartype=V_REAL;
				v->size=1;		/* override any data we parsed in */
				v->msi=v->lsi=0;
				}
			else
			if((v->size>1)&&(v->msi<=0)&&(v->lsi<=0))
				{
				if(v->vartype==V_EVENT) 
					{
					v->size=1;
					}
					else
					{
					/* any criteria for the direction here? */
					v->msi=v->size-1;	
					v->lsi=0;
					}
				}
			else
			if((v->msi>v->lsi)&&((v->msi-v->lsi+1)!=v->size))
				{
				if((v->vartype!=V_EVENT)&&(v->vartype!=V_PARAMETER)) goto err;
				v->size=v->msi-v->lsi+1;
				}
			else
			if((v->lsi>=v->msi)&&((v->lsi-v->msi+1)!=v->size)) 
				{
				if((v->vartype!=V_EVENT)&&(v->vartype!=V_PARAMETER)) goto err;
				v->size=v->msi-v->lsi+1;
				}

			/* initial conditions */
			v->value=(char *)malloc_2(v->size+1);
			v->value[v->size]=0;
			v->narray=(struct Node **)calloc_2(v->size,sizeof(struct Node *));
				{
				int i;
				if(atomic_vectors)
					{
					for(i=0;i<v->size;i++)
						{
						v->value[i]='x';
						}
					v->narray[0]=(struct Node *)calloc_2(1,sizeof(struct Node));
					v->narray[0]->head.time=-1;
					v->narray[0]->head.v.h_val=AN_X;
					}
					else
					{
					for(i=0;i<v->size;i++)
						{
						v->value[i]='x';
	
						v->narray[i]=(struct Node *)calloc_2(1,sizeof(struct Node));
						v->narray[i]->head.time=-1;
						v->narray[i]->head.v.h_val=AN_X;
						}
					}
				}

			if(v->vartype==V_EVENT)
				{
				struct queuedevent *q;
				v->ev=q=(struct queuedevent *)calloc_2(1,sizeof(struct queuedevent));
				q->sym=v;
				q->last_event_time=-1;		
				q->next=queuedevents;
				queuedevents=q;		
				}

			if(!vcdsymroot)
				{
				vcdsymroot=vcdsymcurr=v;
				}
				else
				{
				vcdsymcurr->next=v;
				vcdsymcurr=v;
				}
			numsyms++;

			if(vcd_save_handle)
				{
				if(v->msi==v->lsi)
					{
					if(v->vartype==V_REAL)
						{
						fprintf(vcd_save_handle,"%s\n",v->name);
						}
						else
						{
						if(v->msi>=0)
							{
							if(!vcd_explicit_zero_subscripts)
								fprintf(vcd_save_handle,"%s%c%d\n",v->name,hier_delimeter,v->msi);
								else
								fprintf(vcd_save_handle,"%s[%d]\n",v->name,v->msi);
							}
							else
							{
							fprintf(vcd_save_handle,"%s\n",v->name);
							}
						}
					}
					else
					{
					int i;

					if(!atomic_vectors)
						{
						fprintf(vcd_save_handle,"#%s[%d:%d]",v->name,v->msi,v->lsi);
						if(v->msi>v->lsi)
							{
							for(i=v->msi;i>=v->lsi;i--)
								{
								if(!vcd_explicit_zero_subscripts)
									fprintf(vcd_save_handle," %s%c%d",v->name,hier_delimeter,i);
									else
									fprintf(vcd_save_handle," %s[%d]",v->name,i);
								}
							}
							else
							{
							for(i=v->msi;i<=v->lsi;i++)
								{
								if(!vcd_explicit_zero_subscripts)
									fprintf(vcd_save_handle," %s%c%d",v->name,hier_delimeter,i);
									else
									fprintf(vcd_save_handle," %s[%d]",v->name,i);
								}
							}
						fprintf(vcd_save_handle,"\n");
						}
						else
						{
						fprintf(vcd_save_handle,"%s[%d:%d]\n",v->name,v->msi,v->lsi);
						}
					}
				}

			DEBUG(fprintf(stderr,"VAR %s %d %s %s[%d:%d]\n",
				vartypes[v->vartype], v->size, v->id, v->name, 
					v->msi, v->lsi));
			goto bail;
			err:
			if(v)
				{
				error_count++;
				if(v->name) 
					{
					fprintf(stderr, "Near byte %d, $VAR parse error encountered with '%s'\n", (int)(vcdbyteno+(vst-vcdbuf)), v->name);
					free_2(v->name);
					}
					else
					{
					fprintf(stderr, "Near byte %d, $VAR parse error encountered\n", (int)(vcdbyteno+(vst-vcdbuf)));
					}
				if(v->id) free_2(v->id);
				if(v->value) free_2(v->value);
				free_2(v);
				}

			bail:
			if(vtok!=V_END) sync_end(NULL);
			break;
			}
		case T_ENDDEFINITIONS:
			header_over=1;	/* do symbol table management here */
			create_sorted_table();
			if((!sorted)&&(!indexed))
				{
				fprintf(stderr, "No symbols in VCD file..nothing to do!\n");
				exit(1);
				}
			if(error_count)
				{
				fprintf(stderr, "\n%d VCD parse errors encountered, exiting.\n", error_count);
				exit(1);
				}
			break;
		case T_STRING:
			if(!header_over)
				{
				header_over=1;	/* do symbol table management here */
				create_sorted_table();
				if((!sorted)&&(!indexed)) break;
				}
				{
				/* catchall for events when header over */
				if(yytext[0]=='#')
					{
					TimeType time;
					time=atoi_64(yytext+1);
					
					if(start_time<0)
						{
						start_time=time;
						}

					current_time=time;
					if(end_time<time) end_time=time;	/* in case of malformed vcd files */
					DEBUG(fprintf(stderr,"#"TTFormat"\n",time));
					}
					else
					{
					parse_valuechange();
					}
				}
			break;
		case T_DUMPALL:	/* dump commands modify vals anyway so */
		case T_DUMPPORTSALL:
			break;	/* just loop through..                 */
		case T_DUMPOFF:
		case T_DUMPPORTSOFF:
			dumping_off=1;
			if((!blackout_regions)||((blackout_regions)&&(blackout_regions->bstart<=blackout_regions->bend)))
				{
				struct blackout_region_t *bt = calloc(1, sizeof(struct blackout_region_t));

				bt->bstart = current_time;
				bt->next = blackout_regions;
				blackout_regions = bt;
				}
			break;
		case T_DUMPON:
		case T_DUMPPORTSON:
			dumping_off=0;
			if((blackout_regions)&&(blackout_regions->bstart>blackout_regions->bend))
				{
				blackout_regions->bend = current_time;
				}
			break;
		case T_DUMPVARS:
		case T_DUMPPORTS:
			if(current_time<0)
				{ start_time=current_time=end_time=0; }
			break;
		case T_VCDCLOSE:
			break;	/* next token will be '#' time related followed by $end */
		case T_END:	/* either closure for dump commands or */
			break;	/* it's spurious                       */
		case T_UNKNOWN_KEY:
			sync_end(NULL);	/* skip over unknown keywords */
			break;
		case T_EOF:
			if((blackout_regions)&&(blackout_regions->bstart>blackout_regions->bend))
				{
				blackout_regions->bend = current_time;
				}
			return;
		default:
			DEBUG(fprintf(stderr,"UNKNOWN TOKEN\n"));
		}
	}
}


/*******************************************************************************/

void add_histent(TimeType time, struct Node *n, char ch, int regadd, char *vector)
{
struct HistEnt *he;
char heval;

if(!vector)
{
if(!n->curr)
	{
	he=histent_calloc();
        he->time=-1;
        he->v.h_val=AN_X;

	n->curr=he;
	n->head.next=he;

	add_histent(time,n,ch,regadd, vector);
	}
	else
	{
	if(regadd) { time*=(time_scale); }

	if(ch=='0')              heval=AN_0; else
	if(ch=='1')              heval=AN_1; else
        if((ch=='x')||(ch=='X')) heval=AN_X; else
        if((ch=='z')||(ch=='Z')) heval=AN_Z; else
        if((ch=='h')||(ch=='H')) heval=AN_H; else
        if((ch=='u')||(ch=='U')) heval=AN_U; else
        if((ch=='w')||(ch=='W')) heval=AN_W; else
        if((ch=='l')||(ch=='L')) heval=AN_L; else
        /* if(ch=='-') */        heval=AN_DASH;		/* default */
	
	if((n->curr->v.h_val!=heval)||(time==start_time)||(vcd_preserve_glitches)) /* same region == go skip */ 
        	{
		if(n->curr->time==time)
			{
			DEBUG(printf("Warning: Glitch at time ["TTFormat"] Signal [%p], Value [%c->%c].\n",
				time, n, AN_STR[n->curr->v.h_val], ch));
			n->curr->v.h_val=heval;		/* we have a glitch! */

			num_glitches++;
			if(!(n->curr->flags&HIST_GLITCH))
				{
				n->curr->flags|=HIST_GLITCH;	/* set the glitch flag */
				num_glitch_regions++;
				}
			}
			else
			{
                	he=histent_calloc();
                	he->time=time;
                	he->v.h_val=heval;

                	n->curr->next=he;
			n->curr=he;
                	regions+=regadd;
			}
                }
       }
}
else
{
switch(ch)
	{
	case 's': /* string */
	{
	if(!n->curr)
		{
		he=histent_calloc();
		he->flags=(HIST_STRING|HIST_REAL);
	        he->time=-1;
	        he->v.h_vector=NULL;
	
		n->curr=he;
		n->head.next=he;
	
		add_histent(time,n,ch,regadd, vector);
		}
		else
		{
		if(regadd) { time*=(time_scale); }
	
			if(n->curr->time==time)
				{
				DEBUG(printf("Warning: String Glitch at time ["TTFormat"] Signal [%p].\n",
					time, n));
				if(n->curr->v.h_vector) free_2(n->curr->v.h_vector);
				n->curr->v.h_vector=vector;		/* we have a glitch! */
	
				num_glitches++;
				if(!(n->curr->flags&HIST_GLITCH))
					{
					n->curr->flags|=HIST_GLITCH;	/* set the glitch flag */
					num_glitch_regions++;
					}
				}
				else
				{
	                	he=histent_calloc();
				he->flags=(HIST_STRING|HIST_REAL);
	                	he->time=time;
	                	he->v.h_vector=vector;
	
	                	n->curr->next=he;
				n->curr=he;
	                	regions+=regadd;
				}
	       }
	break;
	}
	case 'g': /* real number */
	{
	if(!n->curr)
		{
		he=histent_calloc();
		he->flags=HIST_REAL;
	        he->time=-1;
	        he->v.h_vector=NULL;
	
		n->curr=he;
		n->head.next=he;
	
		add_histent(time,n,ch,regadd, vector);
		}
		else
		{
		if(regadd) { time*=(time_scale); }
	
		if(
		  (n->curr->v.h_vector&&vector&&(*(double *)n->curr->v.h_vector!=*(double *)vector))
			||(time==start_time)
			||(!n->curr->v.h_vector)
			||(vcd_preserve_glitches)
			) /* same region == go skip */ 
	        	{
			if(n->curr->time==time)
				{
				DEBUG(printf("Warning: Real number Glitch at time ["TTFormat"] Signal [%p].\n",
					time, n));
				if(n->curr->v.h_vector) free_2(n->curr->v.h_vector);
				n->curr->v.h_vector=vector;		/* we have a glitch! */
	
				num_glitches++;
				if(!(n->curr->flags&HIST_GLITCH))
					{
					n->curr->flags|=HIST_GLITCH;	/* set the glitch flag */
					num_glitch_regions++;
					}
				}
				else
				{
	                	he=histent_calloc();
				he->flags=HIST_REAL;
	                	he->time=time;
	                	he->v.h_vector=vector;
	
	                	n->curr->next=he;
				n->curr=he;
	                	regions+=regadd;
				}
	                }
			else
			{
			free_2(vector);
			}
	       }
	break;
	}
	default:
	{
	if(!n->curr)
		{
		he=histent_calloc();
	        he->time=-1;
	        he->v.h_vector=NULL;
	
		n->curr=he;
		n->head.next=he;
	
		add_histent(time,n,ch,regadd, vector);
		}
		else
		{
		if(regadd) { time*=(time_scale); }
	
		if(
		  (n->curr->v.h_vector&&vector&&(strcmp(n->curr->v.h_vector,vector)))
			||(time==start_time)
			||(!n->curr->v.h_vector)
			||(vcd_preserve_glitches)
			) /* same region == go skip */ 
	        	{
			if(n->curr->time==time)
				{
				DEBUG(printf("Warning: Glitch at time ["TTFormat"] Signal [%p], Value [%c->%c].\n",
					time, n, AN_STR[n->curr->v.h_val], ch));
				if(n->curr->v.h_vector) free_2(n->curr->v.h_vector);
				n->curr->v.h_vector=vector;		/* we have a glitch! */
	
				num_glitches++;
				if(!(n->curr->flags&HIST_GLITCH))
					{
					n->curr->flags|=HIST_GLITCH;	/* set the glitch flag */
					num_glitch_regions++;
					}
				}
				else
				{
	                	he=histent_calloc();
	                	he->time=time;
	                	he->v.h_vector=vector;
	
	                	n->curr->next=he;
				n->curr=he;
	                	regions+=regadd;
				}
	                }
			else
			{
			free_2(vector);
			}
	       }
	break;
	}
	}
}

}


static void add_tail_histents(void)
{
int j;
struct vcdsymbol *v;

/* dump out any pending events 1st */
struct queuedevent *q;
q=queuedevents;
while(q)
	{	
	v=q->sym;
	if(current_time!=(v->ev->last_event_time+1))
		{
		/* dump degating event */
		DEBUG(fprintf(stderr,"#"TTFormat" %s = '%c' (event)\n",v->ev->last_event_time+1,v->name,'0'));
		add_histent(v->ev->last_event_time+1,v->narray[0],'0',1, NULL);	
		}
	q=q->next;
	}

/* then do 'x' trailers */

v=vcdsymroot;
while(v)
	{
	if(v->vartype==V_REAL)
		{
		double *d;

		d=malloc_2(sizeof(double));
		*d=1.0;
		add_histent(MAX_HISTENT_TIME-1, v->narray[0], 'g', 0, (char *)d);
		}
	else
	if((v->size==1)||(!atomic_vectors))
	for(j=0;j<v->size;j++)
		{
		add_histent(MAX_HISTENT_TIME-1, v->narray[j], 'x', 0, NULL);
		}
	else
		{
		add_histent(MAX_HISTENT_TIME-1, v->narray[0], 'x', 0, (char *)calloc_2(1,sizeof(char)));
		}

	v=v->next;
	}

v=vcdsymroot;
while(v)
	{
	if(v->vartype==V_REAL)
		{
		double *d;

		d=malloc_2(sizeof(double));
		*d=0.0;
		add_histent(MAX_HISTENT_TIME, v->narray[0], 'g', 0, (char *)d);
		}
	else
	if((v->size==1)||(!atomic_vectors))
	for(j=0;j<v->size;j++)
		{
		add_histent(MAX_HISTENT_TIME, v->narray[j], 'z', 0, NULL);
		}
	else
		{
		add_histent(MAX_HISTENT_TIME, v->narray[0], 'z', 0, (char *)calloc_2(1,sizeof(char)));
		}

	v=v->next;
	}
}

/*******************************************************************************/

static void vcd_build_symbols(void)
{
int j;
int max_slen=-1;
struct sym_chain *sym_chain=NULL, *sym_curr=NULL;
int duphier=0;
char hashdirty;
struct vcdsymbol *v, *vprime;
char *str = NULL;

v=vcdsymroot;
while(v)
	{
	int msi;
	int delta;

		{
		int slen;
		int substnode;

		msi=v->msi;
		delta=((v->lsi-v->msi)<0)?-1:1;
		substnode=0;

		slen=strlen(v->name);
		str=(slen>max_slen)?(wave_alloca((max_slen=slen)+32)):(str); /* more than enough */
		strcpy(str,v->name);

		if(v->msi>=0)
			{
			strcpy(str+slen,vcd_hier_delimeter);
			slen++;
			}

		if((vprime=bsearch_vcd(v->id, strlen(v->id)))!=v) /* hash mish means dup net */
			{
			if(v->size!=vprime->size)
				{
				fprintf(stderr,"ERROR: Duplicate IDs with differing width: %s %s\n", v->name, vprime->name);
				}
				else
				{
				substnode=1;
				}
			}

		if(((v->size==1)||(!atomic_vectors))&&(v->vartype!=V_REAL))
			{
			struct symbol *s = NULL;
	
			for(j=0;j<v->size;j++)
				{
				if(v->msi>=0) 
					{
					if(!vcd_explicit_zero_subscripts)
						sprintf(str+slen,"%d",msi);
						else
						sprintf(str+slen-1,"[%d]",msi);
					}

				hashdirty=0;
				if(symfind(str))
					{
					char *dupfix=(char *)malloc_2(max_slen+32);
					hashdirty=1;
					DEBUG(fprintf(stderr,"Warning: %s is a duplicate net name.\n",str));

					do sprintf(dupfix, "$DUP%d%s%s", duphier++, vcd_hier_delimeter, str);
						while(symfind(dupfix));

					strcpy(str, dupfix);
					free_2(dupfix);
					duphier=0; /* reset for next duplicate resolution */
					}
					/* fallthrough */
					{
					s=symadd(str,hashdirty?hash(str):hashcache);
	
					s->n=v->narray[j];
					if(substnode)
						{
						struct Node *n, *n2;
	
						n=s->n;
						n2=vprime->narray[j];
						/* nname stays same */
						n->head=n2->head;
						n->curr=n2->curr;
						/* harray calculated later */
						n->numhist=n2->numhist;
						}
	
					s->n->nname=s->name;
					s->h=s->n->curr;
					if(!firstnode)
						{
						firstnode=curnode=s;
						}
						else
						{
						curnode->nextinaet=s;
						curnode=s;
						}
	
					numfacs++;
					DEBUG(fprintf(stderr,"Added: %s\n",str));
					}
				msi+=delta;
				}

			if((j==1)&&(v->root))
				{
				s->vec_root=(struct symbol *)v->root;		/* these will get patched over */
				s->vec_chain=(struct symbol *)v->chain;		/* these will get patched over */
				v->sym_chain=s;

				if(!sym_chain)
					{
					sym_curr=(struct sym_chain *)calloc_2(1,sizeof(struct sym_chain));
					sym_chain=sym_curr;
					}
					else
					{
					sym_curr->next=(struct sym_chain *)calloc_2(1,sizeof(struct sym_chain));
					sym_curr=sym_curr->next;
					}
				sym_curr->val=s;
				}
			}
			else	/* atomic vector */
			{
			if(v->vartype!=V_REAL)
				{
				sprintf(str+slen-1,"[%d:%d]",v->msi,v->lsi);
				}
				else
				{
				*(str+slen-1)=0;
				}


			hashdirty=0;
			if(symfind(str))
				{
				char *dupfix=(char *)malloc_2(max_slen+32);
				hashdirty=1;
				DEBUG(fprintf(stderr,"Warning: %s is a duplicate net name.\n",str));

				do sprintf(dupfix, "$DUP%d%s%s", duphier++, vcd_hier_delimeter, str);
					while(symfind(dupfix));

				strcpy(str, dupfix);
				free_2(dupfix);
				duphier=0; /* reset for next duplicate resolution */
				}
				/* fallthrough */
				{
				struct symbol *s;

				s=symadd(str,hashdirty?hash(str):hashcache);	/* cut down on double lookups.. */

				s->n=v->narray[0];
				if(substnode)
					{
					struct Node *n, *n2;

					n=s->n;
					n2=vprime->narray[0];
					/* nname stays same */
					n->head=n2->head;
					n->curr=n2->curr;
					/* harray calculated later */
					n->numhist=n2->numhist;
					n->ext=n2->ext;
					}
					else
					{
					struct ExtNode *en;
					en=(struct ExtNode *)malloc_2(sizeof(struct ExtNode));
					en->msi=v->msi;
					en->lsi=v->lsi;

					s->n->ext=en;
					}

				s->n->nname=s->name;
				s->h=s->n->curr;
				if(!firstnode)
					{
					firstnode=curnode=s;
					}
					else
					{
					curnode->nextinaet=s;
					curnode=s;
					}

				numfacs++;
				DEBUG(fprintf(stderr,"Added: %s\n",str));
				}
			}
		}

	v=v->next;
	}

if(sym_chain)
	{
	sym_curr=sym_chain;	
	while(sym_curr)
		{
		sym_curr->val->vec_root= ((struct vcdsymbol *)sym_curr->val->vec_root)->sym_chain;

		if ((struct vcdsymbol *)sym_curr->val->vec_chain)
			sym_curr->val->vec_chain=((struct vcdsymbol *)sym_curr->val->vec_chain)->sym_chain;

		DEBUG(printf("Link: ('%s') '%s' -> '%s'\n",sym_curr->val->vec_root->name, sym_curr->val->name, sym_curr->val->vec_chain?sym_curr->val->vec_chain->name:"(END)"));

		sym_chain=sym_curr;
		sym_curr=sym_curr->next;
		free_2(sym_chain);
		}
	}
}

/*******************************************************************************/

void vcd_sortfacs(void)
{
int i;

facs=(struct symbol **)malloc_2(numfacs*sizeof(struct symbol *));
curnode=firstnode;
for(i=0;i<numfacs;i++)
        {
        char *subst, ch;
        int len;
                 
        facs[i]=curnode;
        if((len=strlen(subst=curnode->name))>longestname) longestname=len;
        curnode=curnode->nextinaet;
        while((ch=(*subst)))
                {
                if(ch==hier_delimeter) { *subst=0x01; } /* forces sort at hier boundaries */
                subst++;
                }
        }

/* quicksort(facs,0,numfacs-1); */	/* quicksort deprecated because it degenerates on sorted traces..badly.  very badly. */
wave_heapsort(facs,numfacs);

for(i=0;i<numfacs;i++)
        {
        char *subst, ch;
         
        subst=facs[i]->name;
        while((ch=(*subst)))
                {
                if(ch==0x01) { *subst=hier_delimeter; } /* restore back to normal */
                subst++;
                }
        
#ifdef DEBUG_FACILITIES
        printf("%-4d %s\n",i,facs[i]->name);
#endif
        }

facs_are_sorted=1;

init_tree();
for(i=0;i<numfacs;i++)
{                       
build_tree_from_name(facs[i]->name, i);
}                       
treeprune(treeroot);
}

/*******************************************************************************/

static void vcd_cleanup(void)
{
struct slist *s, *s2;
struct vcdsymbol *v, *vt;

if(indexed)
	{
	free_2(indexed); indexed=NULL; 
	}

if(sorted)
	{
	free_2(sorted); sorted=NULL; 
	}

v=vcdsymroot;
while(v)
	{
	if(v->name) free_2(v->name);
	if(v->id) free_2(v->id);
	if(v->value) free_2(v->value);
	if(v->ev) free_2(v->ev);
	if(v->narray) free_2(v->narray);
	vt=v;
	v=v->next;
	free_2(vt);
	}
vcdsymroot=vcdsymcurr=NULL;

if(slisthier) { free_2(slisthier); slisthier=NULL; }
s=slistroot;
while(s)
	{
	s2=s->next;
	if(s->str)free_2(s->str);
	free_2(s);
	s=s2;
	}

slistroot=slistcurr=NULL; slisthier_len=0;
queuedevents=NULL; /* deallocated in the symbol stuff */

if(vcd_is_compressed)
	{
	pclose(vcd_handle);
	}
	else
	{
	fclose(vcd_handle);
	}

if(yytext)
	{
	free_2(yytext);
	yytext=NULL;
	}
}

/*******************************************************************************/

TimeType vcd_main(char *fname)
{
int flen;

pv=rootv=NULL;
vcd_hier_delimeter[0]=hier_delimeter;

errno=0;	/* reset in case it's set for some reason */

yytext=(char *)malloc_2(T_MAX_STR+1);

if(!hier_was_explicitly_set) /* set default hierarchy split char */
	{
	hier_delimeter='.';
	}

flen=strlen(fname);
if (((flen>2)&&(!strcmp(fname+flen-3,".gz")))||
   ((flen>3)&&(!strcmp(fname+flen-4,".zip"))))
	{
	char *str;
	int dlen;
	dlen=strlen(WAVE_DECOMPRESSOR);
	str=wave_alloca(strlen(fname)+dlen+1);
	strcpy(str,WAVE_DECOMPRESSOR);
	strcpy(str+dlen,fname);
	vcd_handle=popen(str,"r");
	vcd_is_compressed=~0;
	}
	else
	{
	if(strcmp("-vcd",fname))
		{
		vcd_handle=fopen(fname,"rb");

		if(vcd_handle)
			{
			fseeko(vcd_handle, 0, SEEK_END);	/* do status bar for vcd load */
			vcd_fsiz = ftello(vcd_handle);
			fseeko(vcd_handle, 0, SEEK_SET);
			}

		if(vcd_warning_filesize < 0) vcd_warning_filesize = VCD_SIZE_WARN;

		if(vcd_warning_filesize)
		if(vcd_fsiz > (vcd_warning_filesize * (1024 * 1024)))
			{
			fprintf(stderr, "Warning! File size is %d MB.  This might fail to load.\n"
					"Consider converting it to lxt, lxt2, or vzt database formats instead.  (See\n"
					"the vcd2lxt(1), vcd2lxt2(1), and vzt2vzt(1) manpages for more information.)\n"
					"To disable this warning, set rc variable vcd_warning_filesize to zero.\n\n",
						(int)(vcd_fsiz/(1024*1024)));
			}
		}
		else
		{
		vcd_handle=stdin;
		}
	vcd_is_compressed=0;
	}

if(!vcd_handle)
	{
	fprintf(stderr, "Error opening %s .vcd file '%s'.\n",
		vcd_is_compressed?"compressed":"", fname);
	exit(1);
	}

/* SPLASH */				splash_create(); 

getch_alloc();		/* alloc membuff for vcd getch buffer */

build_slisthier();

vcd_parse();
if((!sorted)&&(!indexed))
	{
	fprintf(stderr, "No symbols in VCD file..is it malformed?  Exiting!\n");
	exit(1);
	}
add_tail_histents();

if(vcd_save_handle) fclose(vcd_save_handle);

fprintf(stderr, "["TTFormat"] start time.\n["TTFormat"] end time.\n", start_time*time_scale, end_time*time_scale);
if(num_glitches) fprintf(stderr, "Warning: encountered %d glitch%s across %d glitch region%s.\n", 
		num_glitches, (num_glitches!=1)?"es":"",
		num_glitch_regions, (num_glitch_regions!=1)?"s":"");

if(vcd_fsiz)
        {
        splash_sync(vcd_fsiz, vcd_fsiz);
	vcd_fsiz = 0;
        }

vcd_build_symbols();
vcd_sortfacs();
vcd_cleanup();

getch_free();		/* free membuff for vcd getch buffer */

min_time=start_time*time_scale;
max_time=end_time*time_scale;

if((min_time==max_time)||(max_time==0))
        {
        fprintf(stderr, "VCD times range is equal to zero.  Exiting.\n");
        exit(1);
        }

is_vcd=~0;

return(max_time);
}

/*******************************************************************************/
