/*
 * Copyright (c) 2001 Tony Bybell.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the   
 * Software is furnished to do so, subject to the following conditions: 
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*
 * vcd.c 			23jan99ajb
 * evcd parts 			29jun99ajb
 * profiler optimizations 	15jul99ajb
 * stripped out of gtkwave	21jul99ajb
 * fix for duplicate nets       19dec00ajb
 * lxt conversion added		20nov01ajb
 */

#include "v2l_analyzer.h"

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

struct lt_trace *lt=NULL;

int numfacs=0;
int deadcnt=0;

struct symbol **sym=NULL;
struct symbol **facs=NULL;
struct symbol *firstnode=NULL;
struct symbol *curnode=NULL;
TimeType min_time=-1, max_time=-1;
char hier_delimeter='.';
char deadchar='X';

int vcd_explicit_zero_subscripts=-1;  /* 0=yes, -1=no */
char atomic_vectors=1;

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 evcd_strcpy(char *dst, char *src);

static int vcdlineno=1;
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 TimeType time_scale=1;	/* multiplier is 1, 10, 100 */

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

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

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_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",
		 "", "", "" };

#define NUM_TOKENS 17

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;

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

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

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


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

/*
 * alias vs normal symbol adding
 */
static void alias_vs_normal_symadd(struct vcdsymbol *v, struct vcdsymbol *root_v)
{
if(!root_v) 
	{
	if((v->vartype==V_INTEGER)||(v->vartype==V_REAL))
		{
		v->ltsym = lt_symbol_add(lt, v->name, 0, v->msi, v->lsi, (v->vartype==V_INTEGER)?LT_SYM_F_INTEGER:((v->vartype==V_REAL)?LT_SYM_F_DOUBLE:LT_SYM_F_BITS));
		}
		else
		{
		char buf[65537];
		if(v->msi==v->lsi)
			{
			sprintf(buf, "%s[%d]", v->name, v->msi);
			}
			else
			{
			sprintf(buf, "%s[%d:%d]", v->name, v->msi, v->lsi);
			}
		v->ltsym = lt_symbol_add(lt, buf, 0, v->msi, v->lsi, (v->vartype==V_INTEGER)?LT_SYM_F_INTEGER:((v->vartype==V_REAL)?LT_SYM_F_DOUBLE:LT_SYM_F_BITS));
		}
	}
	else
	{
	if((v->vartype==V_INTEGER)||(v->vartype==V_REAL))
		{
		lt_symbol_alias(lt, root_v->name, v->name, v->msi, v->lsi);
		}
		else
		{
		char bufold[65537], buf[65537];
		if(v->msi==v->lsi)
			{
			sprintf(bufold, "%s[%d]", root_v->name, root_v->msi);
			sprintf(buf, "%s[%d]", v->name, v->msi);
			}
			else
			{
			sprintf(bufold, "%s[%d:%d]", root_v->name, root_v->msi, root_v->lsi);
			sprintf(buf, "%s[%d:%d]", v->name, v->msi, v->lsi);
			}
		lt_symbol_alias(lt, bufold, buf, v->msi, v->lsi);
		}
	}
}


/*
 * create sorted (by id) table
 */
static void create_sorted_table(void)
{
struct vcdsymbol *v;
struct vcdsymbol **pnt;
unsigned int vcd_distance;
struct vcdsymbol *root_v;
int i;
                
if(numsyms)
        {
        vcd_distance = vcd_maxid - vcd_minid + 1;
                         
        if(vcd_distance <= 4 * 1024 * 1024)
                {
                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(!(root_v=indexed[v->nid - vcd_minid]))
                                {
                                indexed[v->nid - vcd_minid] = v;
                                }
                        alias_vs_normal_symadd(v, root_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);
                 
                root_v = NULL;
                for(i=0;i<numsyms;i++)
                        {
                        if(sorted[i] != root_v) root_v = NULL;
                        alias_vs_normal_symadd(sorted[i], root_v);
                        }
                }
        }
}

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

/*
 * single char get
 */
static int getch(void)
{
int ch;

ch=fgetc(vcd_handle);
if(ch=='\n') vcdlineno++;
return(((ch==EOF)||(errno))?(-1):(ch));
}

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

if(slisthier)
        {
        free_2(slisthier);
        }

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 line %d, Unknown VCD identifier: '%s'\n",vcdlineno,yytext+1);
				}
				else
				{
				if(v->vartype!=V_EVENT)
					{
					char vl[2];
					vl[0]=yytext[0]; vl[1]=0;
					lt_emit_value_bit_string(lt, v->ltsym, 0, vl);

					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
					{
					char vl[2];

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

					vl[0]='1'; vl[1]=0;
					lt_emit_value_bit_string(lt, v->ltsym, 0, vl);
					vl[0]='0'; vl[1]=0;
					lt_emit_value_bit_string(lt, v->ltsym, 0, vl);

					v->ev->last_event_time=current_time;
					}
				}
			}
			else
			{
			fprintf(stderr,"Near line %d, Malformed VCD identifier\n", vcdlineno);
			}
		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 line %d, Unknown identifier: '%s'\n",vcdlineno, yytext);
			free_2(vector);
			}
			else
			{
			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));
			lt_emit_value_bit_string(lt, v->ltsym, 0, 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);
				free_2(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 line %d, Unknown identifier: '%s'\n",vcdlineno, yytext);
			free_2(vector);
			}
			else
			{
			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));
			lt_emit_value_bit_string(lt, v->ltsym, 0, 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);
				free_2(vector);
				}
			}
		break;


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

		d=malloc_2(sizeof(double));
		*d = 0;
		sscanf(yytext+1,"%lg",d);
		errno = 0;
		
		get_strtoken();

		v=bsearch_vcd(yytext, yylen);	
		if(!v)
			{
			fprintf(stderr,"Near line %d, Unknown identifier: '%s'\n",vcdlineno, yytext);
			free_2(d);
			}
			else
			{
			lt_emit_value_double(lt, v->ltsym, 0, *d);
			lt_emit_value_bit_string(lt, v->ltsym, 0, v->value);
			add_histent(current_time, v->narray[0],'g',1,(char *)d);
			free_2(d);
			}

		break;
		}

	case 's':
	case 'S':
		{
		get_strtoken();	/* simply skip for now */
		break;
		}
	}

}


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(int linear)
{
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=' ';
			int timelogadjust = 0;

			vtok=get_token();
			if((vtok==T_END)||(vtok==T_EOF)) break;
			time_scale=atoi_64(yytext);
			if(!time_scale) time_scale=1;
			switch(time_scale)
				{
				case 10:	timelogadjust = +1;	break;
				case 100:	timelogadjust = +2;	break;
				}

			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 's':
				case ' ': lt_set_timescale(lt,  0+timelogadjust); break;
				case 'm': lt_set_timescale(lt, -3+timelogadjust); break;
				case 'u': lt_set_timescale(lt, -6+timelogadjust); break;
				case 'n': lt_set_timescale(lt, -9+timelogadjust); break;
				case 'p': lt_set_timescale(lt, -12+timelogadjust); break;
				case 'f': lt_set_timescale(lt, -15+timelogadjust); break;
				default:	/* unknown */
					  lt_set_timescale(lt, -9+timelogadjust); break;
				}

			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:
			{
			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);
					strcpy(v->name+slisthier_len+1,yytext);
					}
					else
					{
					v->name=(char *)malloc_2(yylen+1);
					strcpy(v->name,yytext);
					}
				}
				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);
					strcpy(v->name+slisthier_len+1,yytext);
					}
					else
					{
					v->name=(char *)malloc_2(yylen+1);
					strcpy(v->name,yytext);
					}
				
				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)
				{
				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) 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) 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;
				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.val=1;
					}
				}

			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 0
			if((v->vartype==V_INTEGER)||(v->vartype==V_REAL))
				{
				v->ltsym = lt_symbol_add(lt, v->name, 0, v->msi, v->lsi, (v->vartype==V_INTEGER)?LT_SYM_F_INTEGER:((v->vartype==V_REAL)?LT_SYM_F_DOUBLE:LT_SYM_F_BITS));
				}
				else
				{
				char buf[65537];
				if(v->msi==v->lsi)
					{
					sprintf(buf, "%s[%d]", v->name, v->msi);
					}
					else
					{
					sprintf(buf, "%s[%d:%d]", v->name, v->msi, v->lsi);
					}
				v->ltsym = lt_symbol_add(lt, buf, 0, v->msi, v->lsi, (v->vartype==V_INTEGER)?LT_SYM_F_INTEGER:((v->vartype==V_REAL)?LT_SYM_F_DOUBLE:LT_SYM_F_BITS));
				}
#endif

			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)
				{
				if(v->name) free_2(v->name);
				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:
			if(!header_over)
				{
				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(linear)  lt_set_no_interlace(lt);
				}
			break;
		case T_STRING:
			if(header_over)
				{
				/* 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 */
					lt_set_time64(lt, current_time);
					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;
			break;
		case T_DUMPON:
		case T_DUMPPORTSON:
			dumping_off=0;
			break;
		case T_DUMPVARS:
		case T_DUMPPORTS:
                        if(current_time<0)  
                                { start_time=current_time=end_time=0; lt_set_time(lt, current_time); }
                        break;  
		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:
			return;
		default:
			DEBUG(fprintf(stderr,"UNKNOWN TOKEN\n"));
		}
	}
}


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

/*
 * this function doesn't do anything useful anymore except for to mark
 * statistics on the various nets...
 */
void add_histent(TimeType time, struct Node *n, char ch, int regadd, char *vector)
{
struct HistEnt *he;

if(!vector)
{
if(!n->curr)
	{
	he=(struct HistEnt *)calloc_2(1,sizeof(struct HistEnt));
        he->time=-1;
        he->v.val=1;

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

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

	if(toupper(ch)!=deadchar) n->notdead=1;
	n->numtrans++;
       }
}
else
{
if(ch=='g')	/* real number */
	{
	if(!n->curr)
		{
		he=(struct HistEnt *)calloc_2(1,sizeof(struct HistEnt));
	        he->time=-1;
	        he->v.vector=NULL;
	
		n->curr=he;
		n->head.next=he;
	
		add_histent(time,n,ch,regadd, vector);
		}
		else
		{
		n->notdead=1;
		n->numtrans++;
	        }
	}
	else
	{
	if(!n->curr)
		{
		he=(struct HistEnt *)calloc_2(1,sizeof(struct HistEnt));
	        he->time=-1;
	        he->v.vector=NULL;
	
		n->curr=he;
		n->head.next=he;
	
		add_histent(time,n,ch,regadd, vector);
		}
		else
		{
		int i, nlen;
	
		nlen = strlen(vector);
		if(nlen)
			{
			n->numtrans++;
			for(i=0;i<nlen;i++)
				{
				if(toupper(vector[i])!=deadchar)
					{	
					n->notdead=1;
					return;
					}
				}
			}
	       }
	}
}

}


static void add_tail_histents(void)
{
/* dump out any pending events 1st */
struct queuedevent *q;
q=queuedevents;
while(q)
	{	
	struct vcdsymbol *v;

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

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

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;
                 
        facs[i]=curnode;
	subst=curnode->name;
        curnode=curnode->nextinaet;
        while((ch=(*subst)))
                {
                if(ch==hier_delimeter) { *subst=0x01; } /* forces sort at hier boundaries */
                subst++;
                }
        }
quicksort(facs,0,numfacs-1);

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++;
                }
	}

for(i=0;i<numfacs;i++)
	{        
	struct Node *n;
	int do_indent;	

	if(!facs[i]->n->substnode)
		{
		n=facs[i]->n;
		printf("[%c] [%5d] %s", facs[i]->n->notdead?' ':'*',facs[i]->n->numtrans,facs[i]->name);
		if(!facs[i]->n->notdead)
			{
			deadcnt++;
			}
		do_indent=0;
		}
		else
		{
		n=facs[i]->n->substnode;
		printf("[%c] [%5d] %s <-> %s", n->notdead?' ':'*',n->numtrans,facs[i]->name,n->nname);
		if(!n->notdead)
			{
			deadcnt++;
			}
		do_indent=1;
		}

		{
		n=n->substhead;
		while(n)
			{
			if(strcmp(n->nname, facs[i]->name))
				{
				if(do_indent) { printf("\n\t\t\t"); } else { do_indent=1; }
				printf(" <-> %s",n->nname);
				}
			n=n->substhead;
			}
		printf("\n");
		}
        }

printf("\n[%d] total facilities: [%d] facilit%s defined, [%d] facilit%s undefined.\n\n",
	numfacs,numfacs-deadcnt,(numfacs-deadcnt!=1)?"ies":"y",deadcnt,
	(deadcnt!=1)?"ies":"y");
}

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

TimeType vcd_main(char *fname, char *lxname, int dostats, int doclock, int dochg, int dodict, int linear)
{
struct vcdsymbol *v, *v2;

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((strlen(fname)>2)&&(!strcmp(fname+strlen(fname)-3,".gz")))
	{
	char *str;
	int dlen;
	dlen=strlen(WAVE_DECOMPRESSOR);
	str=(char *)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("-",fname))
		{
		vcd_handle=fopen(fname,"rb");
		}
		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);
	}

lt=lt_init(lxname);
if(!lt)
	{
	fprintf(stderr, "Problem opening output file '%s'\n", lxname);
	perror("Why");
	exit(255);
	}

if(doclock) lt_set_clock_compress(lt);
if(dochg)   lt_set_chg_compress(lt);
if(dodict)  lt_set_dict_compress(lt, dodict);
lt_set_initial_value(lt, 'x');
lt_symbol_bracket_stripping(lt, 1);	/* this is intentional */

sym=(struct symbol **)calloc_2(SYMPRIME,sizeof(struct symbol *));
printf("\nConverting VCD File '%s' to LXT file '%s'...\n\n",(vcd_handle!=stdin)?fname:"from stdin", lxname);
build_slisthier();
vcd_parse(linear);

add_tail_histents();

printf("["TTFormat"] start time.\n["TTFormat"] end time.\n\n", start_time, end_time);

lt_close(lt);
lt=NULL;

if (dostats) vcd_sortfacs();

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

if(vcd_handle!=stdin)
        {
        fclose(vcd_handle);
        vcd_handle=NULL;
        }

free(yytext); yytext=NULL;
if(indexed) { free(indexed); indexed=NULL; }
if(sorted) { free(sorted); sorted=NULL; }

v=vcdsymroot;
while(v) 
        {
        if(v->name) { free(v->name); v->name=NULL; }
        if(v->id) { free(v->id); v->id=NULL; }
        if(v->value) { free(v->value); v->value=NULL; }
        
        if(v->narray)
                {
                int i;
                for(i=0;i<v->size;i++)
                        {
                        struct HistEnt *h1, *h2;
         
                        if((h1 = v->narray[i]->head.next))
                                {
                                h1 = v->narray[i]->head.next;
                                while(h1)
                                        {
                                        h2 = h1->next;
                                        free(h1);
                                        h1 = h2;
                                        }
                                }

                        free(v->narray[i]); v->narray[i]=NULL;
                        }
                
                free(v->narray); v->narray=NULL;
                }
         
        v2=v->next;
        free(v);
        v=v2;
        }
vcdsymroot=vcdsymcurr=NULL;
                    
free(sym); sym=NULL;  
                
if(slisthier) { free(slisthier); slisthier=NULL; }

return(max_time);
}

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

/*
 * Generic hash function for symbol names...
 */
int hash(char *s)
{
char *p;
unsigned int h=0, g;
for(p=s;*p;p++)
        {
        h=(h<<4)+(*p);
        if((g=h&0xf0000000))
                {
                h=h^(g>>24);
                h=h^g;
                }
        }
return(h%SYMPRIME);
}

/*
 * add symbol to table.  no duplicate checking
 * is necessary as aet's are "correct."
 */
struct symbol *symadd(char *name, int hv)
{
struct symbol *s;

s=(struct symbol *)calloc_2(1,sizeof(struct symbol));
strcpy(s->name=(char *)malloc_2(strlen(name)+1),name);
s->next=sym[hv];
sym[hv]=s;
return(s);
}

/*
 * find a slot already in the table...
 */
struct symbol *symfind(char *s)
{
int hv;
struct symbol *temp;

        hv=hash(s);
        if(!(temp=sym[hv])) return(NULL); /* no hash entry, add here wanted to add */

        while(temp)
                {
                if(!strcmp(temp->name,s))
                        {
                        return(temp); /* in table already */
                        }
                if(!temp->next) break;
                temp=temp->next;
                }
  
        return(NULL); /* not found, add here if you want to add*/
}

int sigcmp(char *s1, char *s2)         
{
unsigned char c1, c2;
int u1, u2;
                                
for(;;)
        {
        c1=(unsigned char)*(s1++);
        c2=(unsigned char)*(s2++);
                
        if((!c1)&&(!c2)) return(0);
        if((c1<='9')&&(c2<='9')&&(c2>='0')&&(c1>='0'))
                {
                u1=(int)(c1&15);
                u2=(int)(c2&15);
                
                while(((c2=(unsigned char)*s2)>='0')&&(c2<='9'))
                        {
                        u2*=10;
                        u2+=(unsigned int)(c2&15);
                        s2++;
                        }

                while(((c2=(unsigned char)*s1)>='0')&&(c2<='9'))
                        {
                        u1*=10;
                        u1+=(unsigned int)(c2&15);
                        s1++;
                        }
        
                if(u1==u2) continue;
                        else return((int)u1-(int)u2);
                }
                else
                {
                if(c1!=c2) return((int)c1-(int)c2);
                }
        }       
}


int partition(struct symbol **a, int p, int r)
{
struct symbol *x, *t;
int i,j;
                
x=a[p];
i=p-1;
j=r+1;
                 
while(1)
        {
        do
                {
                j--;
                } while(sigcmp(a[j]->name,x->name)>0);

        do      {
                i++;
                } while(sigcmp(a[i]->name,x->name)<0);
 
        if(i<j)
                {
                t=a[i];
                a[i]=a[j];
                a[j]=t; 
                }
                else
                {
                return(j);
                }
        }
}

void quicksort(struct symbol **a, int p, int r)
{
int q;

if(p<r)
        {
        q=partition(a,p,r);
        quicksort(a,p,q);
        quicksort(a,q+1,r);
        }
}


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

int main(int argc, char **argv)
{
int i, dostats=0, doclock=0, dochg=0, dodict=0, linear=0;

if(argc>=3)
        {
	for(i=3;i<argc;i++)
		{
		if(!strcmp(argv[i], "-stats")) dostats = 1;
		else if(!strcmp(argv[i], "-clockpack")) doclock = 1;
		else if(!strcmp(argv[i], "-chgpack")) dochg = 1;
		else if(!strcmp(argv[i], "-linear")) linear = 1;
		else if(!strcmp(argv[i], "-dictpack")) 
			{
			dodict = 1;
			if((i!=(argc-1))&&(argv[i+1][0]!='-'))
				{
				dodict = atoi(argv[++i]);
				}
				else
				{
				printf("-dictpack missing width parameter, defaulting to 16!\n");
				dodict = 16;
				}
			}
		}

        vcd_main(argv[1], argv[2], dostats, doclock, dochg, dodict, linear);
        }
        else
        {
        printf("Usage:\n------\n%s filename.vcd[.gz] filename.lxt [-stats][-clockpack][-chgpack][-linear][-dictpack minwidth]\n"
		"Use \"-\" as a filename to accept uncompressed input from stdin.\n",argv[0]);
        }
exit(0);
}
