/*  GHDL Wavefile reader interface.
    Copyright (C) 2005-2014 Tristan Gingold and Tony Bybell

    GHDL 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, or (at your option) any later
    version.

    GHDL is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with GCC; see the file COPYING.  If not, write to the Free
    Software Foundation, 51 Franklin Street - Suite 500, Boston, MA
    02110-1335, USA.
*/

#include "globals.h"
#include <config.h>
#include "ghw.h"
#include "libghw.h"
#include "tree.h"

/************************ splay ************************/

/*
 * NOTE:
 * a GHW tree's "which" is not the same as a gtkwave "which"
 * in that gtkwave's points to the facs[] array and
 * GHW's functions as an alias handle.  The following
 * (now global) vars are used to resolve those differences...
 *
 * static struct Node **nxp;
 * static struct symbol *sym_head = NULL, *sym_curr = NULL;
 * static int sym_which = 0;
 */

/*
 * pointer splay
 */
typedef struct ghw_tree_node ghw_Tree;
struct ghw_tree_node {
    ghw_Tree * left, * right;
    void *item;
    int val_old;
    struct symbol *sym;
};


long ghw_cmp_l(void *i, void *j)
{
uintptr_t il = (uintptr_t)i, jl = (uintptr_t)j;
return(il - jl);
}

static ghw_Tree * ghw_splay (void *i, ghw_Tree * t) {
/* Simple top down splay, not requiring i to be in the tree t.  */
/* What it does is described above.                             */
    ghw_Tree N, *l, *r, *y;
    int dir;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;

    for (;;) {
	dir = ghw_cmp_l(i, t->item);
	if (dir < 0) {
	    if (t->left == NULL) break;
	    if (ghw_cmp_l(i, t->left->item)<0) {
		y = t->left;                           /* rotate right */
		t->left = y->right;
		y->right = t;
		t = y;
		if (t->left == NULL) break;
	    }
	    r->left = t;                               /* link right */
	    r = t;
	    t = t->left;
	} else if (dir > 0) {
	    if (t->right == NULL) break;
	    if (ghw_cmp_l(i, t->right->item)>0) {
		y = t->right;                          /* rotate left */
		t->right = y->left;
		y->left = t;
		t = y;
		if (t->right == NULL) break;
	    }
	    l->right = t;                              /* link left */
	    l = t;
	    t = t->right;
	} else {
	    break;
	}
    }
    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;
    return t;
}

// Exit the program with return value 1 and print calling line
__attribute__((noreturn)) static void
ghw_error_exit_line (char const *file, int line)
{
  fprintf(stderr, "Failed to load ghw file due to invalid data. Terminating.\n");
  fprintf(stderr, "Error raised at %s:%d.\n", file, line);
  exit(1);
}

#define ghw_error_exit() ghw_error_exit_line(__FILE__, __LINE__)

static ghw_Tree * ghw_insert(void *i, ghw_Tree * t, int val, struct symbol *sym) {
/* Insert i into the tree t, unless it's already there.    */
/* Return a pointer to the resulting tree.                 */
    ghw_Tree * n;
    int dir;

    n = (ghw_Tree *) calloc_2(1, sizeof (ghw_Tree));
    if (n == NULL) {
	fprintf(stderr, "ghw_insert: ran out of memory, exiting.\n");
	exit(255);
    }
    n->item = i;
    n->val_old = val;
    n->sym = sym;
    if (t == NULL) {
	n->left = n->right = NULL;
	return n;
    }
    t = ghw_splay(i,t);
    dir = ghw_cmp_l(i,t->item);
    if (dir<0) {
	n->left = t->left;
	n->right = t;
	t->left = NULL;
	return n;
    } else if (dir>0) {
	n->right = t->right;
	n->left = t;
	t->right = NULL;
	return n;
    } else { /* We get here if it's already in the tree */
             /* Don't add it again                      */
	free_2(n);
	return t;
    }
}


/*
 * chain together bits of the same fac
 */
int strand_pnt(char *s)
{
int len = strlen(s) - 1;
int i;
int rc = -1;

if(s[len] == ']')
	{
	for(i=len-1;i>0;i--)
		{
		if(((s[i]>='0') && (s[i]<='9')) || (s[i] == '-')) continue;
		if(s[i] != '[') break;
		return(i);	/* position right before left bracket for strncmp */
		}
	}

return(rc);
}

void rechain_facs(void)
{
int i;
struct symbol *psr = NULL;
struct symbol *root = NULL;

for(i=0;i<GLOBALS->numfacs;i++)
	{
	if(psr)
		{
		int ev1 = GLOBALS->facs[i-1]->n->extvals;
		int ev2 = GLOBALS->facs[i]->n->extvals;

		if(!ev1 && !ev2)
			{
			char *fstr1 = GLOBALS->facs[i-1]->name;
			char *fstr2 = GLOBALS->facs[i]->name;
			int p1 = strand_pnt(fstr1);
			int p2 = strand_pnt(fstr2);

			if(!root)
				{
				if((p1>=0)&&(p1==p2))
					{
					if(!strncmp(fstr1, fstr2, p1))
						{
						root = GLOBALS->facs[i-1];
						root->vec_root = root;
						root->vec_chain = GLOBALS->facs[i];
						GLOBALS->facs[i]->vec_root = root;
						}
					}
				}
				else
				{
				if((p1>=0)&&(p1==p2))
					{
					if(!strncmp(fstr1, fstr2, p1))
						{
						psr->vec_chain = GLOBALS->facs[i];
						GLOBALS->facs[i]->vec_root = root;
						}
						else
						{
						root = NULL;
						}
					}
					else
					{
					root = NULL;
					}
				}
			}
			else
			{
			root = NULL;
			}
		}

	psr = GLOBALS->facs[i];
	}
}


/*
 * preserve tree->t_which ordering so hierarchy children index pointers don't get corrupted
 */

#if 1

/* limited recursive version */

static void recurse_tree_build_whichcache(struct tree *t)
{
if(t)
	{
	struct tree *t2 = t;
	int i;
	int cnt = 1;
	struct tree **ar;

	while((t2 = t2->next)) { cnt++; }

	ar = malloc_2(cnt * sizeof(struct tree *));
	t2 = t;
	for(i=0;i<cnt;i++)
		{
		ar[i] = t2;
		if(t2->child) { recurse_tree_build_whichcache(t2->child); }
		t2 = t2->next;
		}

	for(i=cnt-1;i>=0;i--)
		{
		t = ar[i];
		if(t->t_which >= 0)
			{
			GLOBALS->gwt_ghw_c_1 = ghw_insert(t, GLOBALS->gwt_ghw_c_1, t->t_which, GLOBALS->facs[t->t_which]);
			}
		}

	free_2(ar);
	}
}

static void recurse_tree_fix_from_whichcache(struct tree *t)
{
if(t)
	{
	struct tree *t2 = t;
	int i;
	int cnt = 1;
	struct tree **ar;

	while((t2 = t2->next)) { cnt++; }

	ar = malloc_2(cnt * sizeof(struct tree *));
	t2 = t;
	for(i=0;i<cnt;i++)
		{
		ar[i] = t2;
		if(t2->child) { recurse_tree_fix_from_whichcache(t2->child); }
		t2 = t2->next;
		}

	for(i=cnt-1;i>=0;i--)
		{
		t = ar[i];
		if(t->t_which >= 0)
			{
			GLOBALS->gwt_ghw_c_1 = ghw_splay(t, GLOBALS->gwt_ghw_c_1);
			GLOBALS->gwt_corr_ghw_c_1 = ghw_splay(GLOBALS->gwt_ghw_c_1->sym, GLOBALS->gwt_corr_ghw_c_1); /* all facs are in this tree so this is OK */

			t->t_which = GLOBALS->gwt_corr_ghw_c_1->val_old;
			}
		}

	free_2(ar);
	}
}

#else

/* original fully-recursive version */

static void recurse_tree_build_whichcache(struct tree *t)
{
if(t)
        {
        if(t->child) { recurse_tree_build_whichcache(t->child); }
        if(t->next) { recurse_tree_build_whichcache(t->next); }

        if(t->t_which >= 0) GLOBALS->gwt_ghw_c_1 = ghw_insert(t, GLOBALS->gwt_ghw_c_1, t->t_which, GLOBALS->facs[t->t_which]);
        }
}

static void recurse_tree_fix_from_whichcache(struct tree *t)
{
if(t)
        {
        if(t->child) { recurse_tree_fix_from_whichcache(t->child); }
        if(t->next) { recurse_tree_fix_from_whichcache(t->next); }

        if(t->t_which >= 0)
                {
                GLOBALS->gwt_ghw_c_1 = ghw_splay(t, GLOBALS->gwt_ghw_c_1);
                GLOBALS->gwt_corr_ghw_c_1 = ghw_splay(GLOBALS->gwt_ghw_c_1->sym, GLOBALS->gwt_corr_ghw_c_1); /* all facs are in this tree so this is OK */

                t->t_which = GLOBALS->gwt_corr_ghw_c_1->val_old;
                }
        }
}

#endif

static void incinerate_whichcache_tree(ghw_Tree *t)
{
if(t->left) incinerate_whichcache_tree(t->left);
if(t->right) incinerate_whichcache_tree(t->right);

free_2(t);
}


/*
 * sort facs and also cache/reconstruct tree->t_which pointers
 */
static void ghw_sortfacs(void)
{
int i;

recurse_tree_build_whichcache(GLOBALS->treeroot);

for(i=0;i<GLOBALS->numfacs;i++)
        {
        char *subst;
#ifdef WAVE_HIERFIX
        char ch;
#endif
        int len;
        struct symbol *curnode = GLOBALS->facs[i];

	subst=curnode->name;
        if((len=strlen(subst))>GLOBALS->longestname) GLOBALS->longestname=len;
#ifdef WAVE_HIERFIX
        while((ch=(*subst)))
                {
                if(ch==GLOBALS->hier_delimeter) { *subst=VCDNAM_HIERSORT; } /* forces sort at hier boundaries */
                subst++;
                }
#endif
        }

wave_heapsort(GLOBALS->facs,GLOBALS->numfacs);

for(i=0;i<GLOBALS->numfacs;i++)
	{
	GLOBALS->gwt_corr_ghw_c_1 = ghw_insert(GLOBALS->facs[i], GLOBALS->gwt_corr_ghw_c_1, i, NULL);
	}

recurse_tree_fix_from_whichcache(GLOBALS->treeroot);
if(GLOBALS->gwt_ghw_c_1)
	{
	incinerate_whichcache_tree(GLOBALS->gwt_ghw_c_1); GLOBALS->gwt_ghw_c_1 = NULL;
	}
if(GLOBALS->gwt_corr_ghw_c_1)
	{
	incinerate_whichcache_tree(GLOBALS->gwt_corr_ghw_c_1); GLOBALS->gwt_corr_ghw_c_1 = NULL;
	}

#ifdef WAVE_HIERFIX
for(i=0;i<GLOBALS->numfacs;i++)
        {
        char *subst, ch;

        subst=GLOBALS->facs[i]->name;
        while((ch=(*subst)))
                {
                if(ch==VCDNAM_HIERSORT) { *subst=GLOBALS->hier_delimeter; } /* restore back to normal */
                subst++;
                }
        }
#endif

GLOBALS->facs_are_sorted=1;
}

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



static struct tree *
build_hierarchy_type (struct ghw_handler *h, union ghw_type *t,
		      const char *pfx, unsigned int **sig);

static struct tree *
build_hierarchy_record (struct ghw_handler *h, const char *pfx,
			unsigned nbr_els, struct ghw_record_element *els,
			unsigned int **sig)
{
  struct tree *res;
  struct tree *last;
  struct tree *c;
  unsigned int i;

  res = (struct tree *) calloc_2(1, sizeof (struct tree) + strlen(pfx) + 1);
  strcpy(res->name, (char *)pfx);
  res->t_which = WAVE_T_WHICH_UNDEFINED_COMPNAME;

  last = NULL;
  for (i = 0; i < nbr_els; i++)
    {
      c = build_hierarchy_type (h, els[i].type, els[i].name, sig);
      if (last == NULL)
	res->child = c;
      else
	last->next = c;
      last = c;
    }
  return res;
}

static void
build_hierarchy_array (struct ghw_handler *h, union ghw_type *arr, int dim,
		       const char *pfx, struct tree **res, unsigned int **sig)
{
  union ghw_type *idx;
  struct ghw_type_array *base =
    (struct ghw_type_array *) ghw_get_base_type (arr->sa.base);
  char *name = NULL;

  if ((unsigned int)dim == base->nbr_dim)
    {
      struct tree *t;
      sprintf (GLOBALS->asbuf, "%s]", pfx);
      name = strdup_2(GLOBALS->asbuf);

      t = build_hierarchy_type (h, arr->sa.el, name, sig);

      free_2(name); /* memory leak from GH commit 7ae937b on devel branch */

      if (*res != NULL)
	(*res)->next = t;
      *res = t;
      return;
    }

  idx = ghw_get_base_type (base->dims[dim]);
  switch (idx->kind)
    {
    case ghdl_rtik_type_i32:
      {
	int32_t v;
	char *nam;
	struct ghw_range_i32 *r;
	/* struct tree *last; */
	int len;

	/* last = NULL; */
	if (arr->sa.rngs[dim]->kind != ghdl_rtik_type_i32) ghw_error_exit();
	r = &arr->sa.rngs[dim]->i32;
	len = ghw_get_range_length ((union ghw_range *)r);
	if (len <= 0)
	  break;
	v = r->left;
	while (1)
	  {
	    sprintf(GLOBALS->asbuf, "%s%c"GHWPRI32,
                    pfx, dim == 0 ? '[' : ',', v);
            nam = strdup_2(GLOBALS->asbuf);
	    build_hierarchy_array (h, arr, dim + 1, nam, res, sig);
	    free_2(nam);
	    if (v == r->right)
	      break;
	    if (r->dir == 0)
	      v++;
	    else
	      v--;
	  }
      }
      return;

    case ghdl_rtik_type_e8:
      {
	int32_t v;
	char *nam;
	struct ghw_range_e8 *r;
	/* struct tree *last; */
	int len;

	/* last = NULL; */
	if (arr->sa.rngs[dim]->kind != ghdl_rtik_type_e8) ghw_error_exit();
	r = &arr->sa.rngs[dim]->e8;
	len = ghw_get_range_length ((union ghw_range *)r);
	if (len <= 0)
	  break;
	v = r->left;
	while (1)
	  {
	    sprintf(GLOBALS->asbuf, "%s%c"GHWPRI32,
                    pfx, dim == 0 ? '[' : ',', v);
            nam = strdup_2(GLOBALS->asbuf);
	    build_hierarchy_array (h, arr, dim + 1, nam, res, sig);
	    free_2(nam);
	    if (v == r->right)
	      break;
	    if (r->dir == 0)
	      v++;
	    else
	      v--;
	  }
      }
      return;
    /* PATCH-BEGIN: */
    case ghdl_rtik_type_b2:
      {
	int32_t v;
	char *nam;
	struct ghw_range_b2 *r;
	/* struct tree *last; */
	int len;

	/* last = NULL; */
	if (arr->sa.rngs[dim]->kind != ghdl_rtik_type_b2) ghw_error_exit();
	r = &arr->sa.rngs[dim]->b2;
	len = ghw_get_range_length ((union ghw_range *)r);
	if (len <= 0)
	  break;
	v = r->left;
	while (1)
	  {
	    sprintf(GLOBALS->asbuf, "%s%c"GHWPRI32,
                    pfx, dim == 0 ? '[' : ',', v);
            nam = strdup_2(GLOBALS->asbuf);
	    build_hierarchy_array (h, arr, dim + 1, nam, res, sig);
	    free_2(nam);
	    if (v == r->right)
	      break;
	    if (r->dir == 0)
	      v++;
	    else
	      v--;
	  }
      }
      return;
      /* PATCH-END: */
    default:
      fprintf (stderr, "build_hierarchy_array: unhandled type %d\n",
	       idx->kind);
      abort ();
    }
}

static struct tree *
build_hierarchy_type (struct ghw_handler *h, union ghw_type *t,
		      const char *pfx, unsigned int **sig)
{
  struct tree *res;
  struct symbol *s;

  switch (t->kind)
    {
    case ghdl_rtik_subtype_scalar:
      return build_hierarchy_type (h, t->ss.base, pfx, sig);
    case ghdl_rtik_type_b2:
    case ghdl_rtik_type_e8:
    case ghdl_rtik_type_f64:
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_p32:
    case ghdl_rtik_type_p64:

      s = calloc_2(1, sizeof(struct symbol));

        if(!GLOBALS->firstnode)
                {
                GLOBALS->firstnode=
                GLOBALS->curnode=calloc_2(1, sizeof(struct symchain));
                }
                else
                {
                GLOBALS->curnode->next=calloc_2(1, sizeof(struct symchain));
                GLOBALS->curnode=GLOBALS->curnode->next;
                }
        GLOBALS->curnode->symbol=s;

      GLOBALS->nbr_sig_ref_ghw_c_1++;
      res = (struct tree *) calloc_2(1, sizeof (struct tree) + strlen(pfx) + 1);
      strcpy(res->name, (char *)pfx);
      // last element is GHW_NO_SIG, don't increment beyond it
      if (**sig == GHW_NO_SIG) ghw_error_exit();
      res->t_which = *(*sig)++;

      size_t nxp_idx = (size_t)res->t_which;
      if (nxp_idx >= GLOBALS->nbr_sigs_ghw_c_1) ghw_error_exit();
      s->n = GLOBALS->nxp_ghw_c_1[nxp_idx];
      return res;
    case ghdl_rtik_subtype_array:
    case ghdl_rtik_subtype_array_ptr:
      {
	struct tree *r;
	res = (struct tree *) calloc_2(1, sizeof (struct tree) + strlen(pfx) + 1);
	strcpy(res->name, (char *)pfx);
	res->t_which = WAVE_T_WHICH_UNDEFINED_COMPNAME;
	r = res;
	build_hierarchy_array (h, t, 0, "", &res, sig);
	r->child = r->next;
	r->next = NULL;
	return r;
      }
    case ghdl_rtik_type_record:
      return build_hierarchy_record(h, pfx, t->rec.nbr_fields, t->rec.els, sig);
    case ghdl_rtik_subtype_record:
      return build_hierarchy_record
	(h, pfx, t->sr.base->nbr_fields, t->sr.els, sig);
    default:
      fprintf (stderr, "build_hierarchy_type: unhandled type %d\n", t->kind);
      abort ();
    }
}

/* Create the gtkwave tree from the GHW hierarchy.  */

static struct tree *
build_hierarchy (struct ghw_handler *h, struct ghw_hie *hie)
{
  struct tree *t;
  struct tree *t_ch;
  struct tree *prev;
  struct ghw_hie *ch;
  unsigned char ttype;

  switch (hie->kind)
    {
    case ghw_hie_design:
    case ghw_hie_block:
    case ghw_hie_instance:
    case ghw_hie_generate_for:
    case ghw_hie_generate_if:
    case ghw_hie_package:

      /* Convert kind.  */
      switch(hie->kind)
		{
	    	case ghw_hie_design:		ttype = TREE_VHDL_ST_DESIGN; break;
    		case ghw_hie_block:		ttype = TREE_VHDL_ST_BLOCK; break;
    		case ghw_hie_instance:		ttype = TREE_VHDL_ST_INSTANCE; break;
    		case ghw_hie_generate_for:	ttype = TREE_VHDL_ST_GENFOR; break;
    		case ghw_hie_generate_if:	ttype = TREE_VHDL_ST_GENIF; break;
    		case ghw_hie_package:
		default:
						ttype = TREE_VHDL_ST_PACKAGE; break;
		}

      /* For iterative generate, add the index.  */
      if (hie->kind == ghw_hie_generate_for)
	{
	  char buf[128];
	  int name_len, buf_len;
	  char *n;

	  ghw_get_value (buf, sizeof (buf),
			 hie->u.blk.iter_value, hie->u.blk.iter_type);
	  name_len = strlen (hie->name);
	  buf_len = strlen (buf);

          t = (struct tree *) calloc_2(1, sizeof (struct tree) + (2 + buf_len + name_len + 1));
	  t->kind = ttype;
	  n = t->name;

	  memcpy (n, hie->name, name_len);
	  n += name_len;
	  *n++ = '[';
	  memcpy (n, buf, buf_len);
	  n += buf_len;
	  *n++ = ']';
	  *n = 0;
	}
      else
        {
          if(hie->name)
		{
          	t = (struct tree *) calloc_2(1, sizeof (struct tree) + strlen(hie->name) + 1);
	  	t->kind = ttype;
          	strcpy(t->name, (char *)hie->name);
		}
		else
		{
          	t = (struct tree *) calloc_2(1, sizeof (struct tree) + 1);
	  	t->kind = ttype;
		}
        }

      t->t_which = WAVE_T_WHICH_UNDEFINED_COMPNAME;

      /* Recurse.  */
      prev = NULL;
      for (ch = hie->u.blk.child; ch != NULL; ch = ch->brother)
	{
	  t_ch = build_hierarchy (h, ch);
	  if (t_ch != NULL)
	    {
	      if (prev == NULL)
		t->child = t_ch;
	      else
		prev->next = t_ch;
	      prev = t_ch;
	    }
	}
      return t;
    case ghw_hie_process:
      return NULL;
    case ghw_hie_signal:
    case ghw_hie_port_in:
    case ghw_hie_port_out:
    case ghw_hie_port_inout:
    case ghw_hie_port_buffer:
    case ghw_hie_port_linkage:
      {
	unsigned int *ptr = hie->u.sig.sigs;

        /* Convert kind.  */
        switch(hie->kind)
		{
		case ghw_hie_signal:		ttype = TREE_VHDL_ST_SIGNAL; break;
		case ghw_hie_port_in:		ttype = TREE_VHDL_ST_PORTIN; break;
		case ghw_hie_port_out:		ttype = TREE_VHDL_ST_PORTOUT; break;
		case ghw_hie_port_inout:	ttype = TREE_VHDL_ST_PORTINOUT; break;
		case ghw_hie_port_buffer:	ttype = TREE_VHDL_ST_BUFFER; break;
		case ghw_hie_port_linkage:
		default:
						ttype = TREE_VHDL_ST_LINKAGE; break;
		}

        /* Convert type.  */
	t = build_hierarchy_type (h, hie->u.sig.type, hie->name, &ptr);
	if (*ptr != 0)
	  abort ();
	if(t) { t->kind = ttype; }
	return t;
      }
    default:
      fprintf (stderr, "ghw: build_hierarchy: cannot handle hie %d\n",
	       hie->kind);
      abort ();
    }
}

void
facs_debug (void)
{
  int i;
  struct Node *n;

  for (i = 0; i < GLOBALS->numfacs; i++)
    {
      hptr h;

      n = GLOBALS->facs[i]->n;
      printf ("%d: %s\n", i, n->nname);
      if (n->extvals)
	printf ("  ext: %d - %d\n", n->msi, n->lsi);
      for (h = &n->head; h; h = h->next)
	printf ("  time:"TTFormat" flags:%02x vect:%p\n",
		h->time, h->flags, h->v.h_vector);
    }
}


static void
create_facs (struct ghw_handler *h)
{
  unsigned int i;
  struct symchain *sc = GLOBALS->firstnode;

  GLOBALS->numfacs = GLOBALS->nbr_sig_ref_ghw_c_1;
  GLOBALS->facs=(struct symbol **)malloc_2(GLOBALS->numfacs*sizeof(struct symbol *));

  i = 0;
  while(sc)
	{
	GLOBALS->facs[i++] = sc->symbol;
	sc = sc->next;
	}

  for (i = 0; i < h->nbr_sigs; i++)
    {
      struct Node *n = GLOBALS->nxp_ghw_c_1[i];

      if (h->sigs[i].type)
	switch (h->sigs[i].type->kind)
	  {
	  case ghdl_rtik_type_b2:
	    if (h->sigs[i].type->en.wkt == ghw_wkt_bit)
	      {
		n->extvals = 0;
		break;
	      }
	    /* FALLTHROUGH */
	  case ghdl_rtik_type_e8:
	    if (GLOBALS->xlat_1164_ghw_c_1 && h->sigs[i].type->en.wkt == ghw_wkt_std_ulogic)
	      {
		n->extvals = 0;
		break;
	      }
	    /* FALLTHROUGH */
	  case ghdl_rtik_type_i32:
	  case ghdl_rtik_type_p32:
	    n->extvals = 1;
	    n->msi = 31; n->lsi = 0;
	    n->vartype = ND_VCD_INTEGER;
	    break;
	  case ghdl_rtik_type_i64:
	  case ghdl_rtik_type_p64:
	    n->extvals = 1;
	    n->msi = 63; n->lsi = 0;
	    n->vartype = ND_VCD_INTEGER;
	    break;
	  case ghdl_rtik_type_e32: /* ajb: what is e32? */
	  case ghdl_rtik_type_f64:
	    n->extvals = 1;
	    n->msi = n->lsi = 0;
	    break;
	  default:
	    fprintf (stderr, "ghw:create_facs: unhandled kind %d\n",
		     h->sigs[i].type->kind);
	    n->extvals = 0;
	  }
    }

}




static void
set_fac_name_1 (struct tree *t)
{
  for (; t != NULL; t = t->next)
    {
      int prev_len = GLOBALS->fac_name_len_ghw_c_1;

      /* Complete the name.  */
      if(t->name[0]) /* originally (t->name != NULL) when using pointers */
	{
	  int len;

	  len = strlen (t->name) + 1;
	  if (len + GLOBALS->fac_name_len_ghw_c_1 >= GLOBALS->fac_name_max_ghw_c_1)
	    {
	      GLOBALS->fac_name_max_ghw_c_1 *= 2;
	      if (GLOBALS->fac_name_max_ghw_c_1 <= len + GLOBALS->fac_name_len_ghw_c_1)
		GLOBALS->fac_name_max_ghw_c_1 = len + GLOBALS->fac_name_len_ghw_c_1 + 1;
	      GLOBALS->fac_name_ghw_c_1 = realloc_2(GLOBALS->fac_name_ghw_c_1, GLOBALS->fac_name_max_ghw_c_1);
	    }
          if(t->name[0] != '[')
		{
	  	GLOBALS->fac_name_ghw_c_1[GLOBALS->fac_name_len_ghw_c_1] = '.';
	  	/* The NUL is copied, since LEN is 1 + strlen.  */
	  	memcpy (GLOBALS->fac_name_ghw_c_1 + GLOBALS->fac_name_len_ghw_c_1 + 1, t->name, len);
	  	GLOBALS->fac_name_len_ghw_c_1 += len;
		}
		else
		{
	  	memcpy (GLOBALS->fac_name_ghw_c_1 + GLOBALS->fac_name_len_ghw_c_1, t->name, len);
	  	GLOBALS->fac_name_len_ghw_c_1 += (len - 1);
		}
	}

      if (t->t_which >= 0)
	{
        struct symchain *sc = GLOBALS->firstnode;
	struct symbol *s = sc->symbol;

	s->name = strdup_2(GLOBALS->fac_name_ghw_c_1);
	size_t nxp_idx = (size_t)t->t_which;
	if (nxp_idx > GLOBALS->nbr_sigs_ghw_c_1) ghw_error_exit();
	s->n = GLOBALS->nxp_ghw_c_1[nxp_idx];
	if(!s->n->nname) s->n->nname = s->name;

	t->t_which = GLOBALS->sym_which_ghw_c_1++; /* patch in gtkwave "which" as node is correct */

	GLOBALS->curnode = GLOBALS->firstnode->next;
	free_2(GLOBALS->firstnode);
	GLOBALS->firstnode = GLOBALS->curnode;
	}

      if (t->child)
	set_fac_name_1 (t->child);

      /* Revert name.  */
      GLOBALS->fac_name_len_ghw_c_1 = prev_len;
      GLOBALS->fac_name_ghw_c_1[GLOBALS->fac_name_len_ghw_c_1] = 0;
    }
}

static void
set_fac_name (struct ghw_handler *h)
{
  if (GLOBALS->fac_name_max_ghw_c_1 == 0)
    {
      GLOBALS->fac_name_max_ghw_c_1 = 1024;
      GLOBALS->fac_name_ghw_c_1 = malloc_2(GLOBALS->fac_name_max_ghw_c_1);
    }
  GLOBALS->fac_name_len_ghw_c_1 = 3;
  memcpy (GLOBALS->fac_name_ghw_c_1, "top", 4);
  GLOBALS->last_fac_ghw_c_1 = h->nbr_sigs;
  set_fac_name_1 (GLOBALS->treeroot);
}

static void
add_history (struct ghw_handler *h, struct Node *n, int sig_num)
{
  struct HistEnt *he;
  struct ghw_sig *sig = &h->sigs[sig_num];
  union ghw_type *sig_type = sig->type;
  int flags;
  int is_vector = 0;
#ifdef WAVE_HAS_H_DOUBLE
  int is_double = 0;
#endif

  if (sig_type == NULL)
    return;

  GLOBALS->regions++;

  switch (sig_type->kind)
    {
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_p32:
    case ghdl_rtik_type_p64:
      flags = 0; break;

    case ghdl_rtik_type_b2:
      if (sig_type->en.wkt == ghw_wkt_bit)
	{
	  flags = 0;
	  break;
	}
      /* FALLTHROUGH */
    case ghdl_rtik_type_e8:
      if (GLOBALS->xlat_1164_ghw_c_1 && sig_type->en.wkt == ghw_wkt_std_ulogic)
	{
	  flags = 0;
	  break;
	}
      /* FALLTHROUGH */
    case ghdl_rtik_type_e32:
      flags = HIST_STRING|HIST_REAL;
      if (HIST_STRING == 0)
	{
	  if (!GLOBALS->warned_ghw_c_1)
	    fprintf (stderr, "warning: do not compile with STRICT_VCD\n");
	  GLOBALS->warned_ghw_c_1 = 1;
	  return;
	}
      break;
    case ghdl_rtik_type_f64:
      flags = HIST_REAL;
      break;
    default:
      fprintf (stderr, "ghw:add_history: unhandled kind %d\n",
	       sig->type->kind);
      return;
    }

  if(!n->curr)
    	{
      	he=histent_calloc();
      	he->flags = flags;
      	he->time=-1;
      	he->v.h_vector=NULL;

      	n->head.next=he;
      	n->curr=he;
      	n->head.time = -2;
    	}

  he=histent_calloc();
  he->flags = flags;
  he->time=h->snap_time;

  switch (sig_type->kind)
    {
    case ghdl_rtik_type_b2:
      if (sig_type->en.wkt == ghw_wkt_bit)
	he->v.h_val = sig->val->b2 == 0 ? AN_0 : AN_1;
      else
        {
        if (sig->val->b2 >= sig->type->en.nbr) ghw_error_exit();
        he->v.h_vector = (char *)sig->type->en.lits[sig->val->b2];
	is_vector = 1;
        }
      break;
    case ghdl_rtik_type_e8:
      {
      unsigned char val_e8 = sig->val->e8;
      if (GLOBALS->xlat_1164_ghw_c_1 && sig_type->en.wkt == ghw_wkt_std_ulogic)
	{
	  /* Res: 0->0, 1->X, 2->Z, 3->1 */
          static const char map_su2vlg[9] = {
            /* U */ AN_U, /* X */ AN_X, /* 0 */ AN_0, /* 1 */ AN_1,
            /* Z */ AN_Z, /* W */ AN_W, /* L */ AN_L, /* H */ AN_H,
            /* - */ AN_DASH
          };
          if (val_e8 >= sizeof(map_su2vlg)/sizeof(map_su2vlg[0])) ghw_error_exit();
	  he->v.h_val = map_su2vlg[val_e8];
	}
      else
        {
        if (val_e8 >= sig_type->en.nbr) ghw_error_exit();
	he->v.h_vector = (char *)sig_type->en.lits[val_e8];
	is_vector = 1;
        }
      break;
      }
    case ghdl_rtik_type_f64:
      {
#ifdef WAVE_HAS_H_DOUBLE
        he->v.h_double = sig->val->f64;
        is_double = 1;
#else
	double *d = malloc_2(sizeof (double));
	*d = sig->val->f64;
	he->v.h_vector = (char *)d;
        is_vector = 1;
#endif
      }
      break;
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_p32:
	{
	int i;
	he->v.h_vector = malloc_2(32);
	for(i=0;i<32;i++)
		{
		he->v.h_vector[31-i] = ((sig->val->i32 >> i) & 1) ? AN_1: AN_0;
		}
	}
      is_vector = 1;
      break;
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_p64:
	{
	int i;
	he->v.h_vector = malloc_2(64);
	for(i=0;i<64;i++)
		{
		he->v.h_vector[63-i] = ((sig->val->i64 >> i) & 1) ? AN_1: AN_0;
		}
	}
      is_vector = 1;
      break;
    default:
      abort ();
    }

    /* deglitch */
    if(n->curr->time == he->time)
	{
	int gl_add = 0;

	if(n->curr->time) /* filter out time zero glitches */
		{
		gl_add = 1;
		}

        GLOBALS->num_glitches_ghw_c_1 += gl_add;

	if(!(n->curr->flags&HIST_GLITCH))
        	{
		if(gl_add)
			{
	                n->curr->flags|=HIST_GLITCH;    /* set the glitch flag */
	                GLOBALS->num_glitch_regions_ghw_c_1++;
			}
                }

#ifdef WAVE_HAS_H_DOUBLE
	if(is_double)
		{
		n->curr->v.h_double = he->v.h_double;
		}
	else
#endif
        if(is_vector)
                {
                if(n->curr->v.h_vector &&
                   sig_type->kind != ghdl_rtik_type_b2 &&
                   sig_type->kind != ghdl_rtik_type_e8) free_2(n->curr->v.h_vector);
                n->curr->v.h_vector = he->v.h_vector;
                /* can't free up this "he" because of block allocation so assume it's dead */
                }
		else
		{
		n->curr->v.h_val = he->v.h_val;
		}
	return;
	}
	else /* look for duplicate dumps of same value at adjacent times */
	{
	if(!is_vector
#ifdef WAVE_HAS_H_DOUBLE
		& !is_double
#endif
	  )
		{
		if(n->curr->v.h_val == he->v.h_val)
			{
			return;
	                /* can't free up this "he" because of block allocation so assume it's dead */
			}
		}
	}

  n->curr->next=he;
  n->curr=he;
}

static void
add_tail (struct ghw_handler *h)
{
  unsigned int i;
  TimeType j;

  for (j = 1; j>=0 ; j--) /* add two endcaps */
  for (i = 0; i < h->nbr_sigs; i++)
    {
      struct ghw_sig *sig = &h->sigs[i];
      struct Node *n = GLOBALS->nxp_ghw_c_1[i];
      struct HistEnt *he;

      if (sig->type == NULL || n == NULL || !n->curr)
	continue;

      /* Copy the last one.  */
      he = histent_calloc();
      *he = *n->curr;
      he->time = MAX_HISTENT_TIME - j;
      he->next = NULL;

      /* Append.  */
      n->curr->next=he;
      n->curr=he;
    }
}

static void
read_traces (struct ghw_handler *h)
{
  int *list;
  unsigned int i;
  enum ghw_res res;

  list = malloc_2((GLOBALS->numfacs + 1) * sizeof (int));

  while (1)
    {
      res = ghw_read_sm_hdr (h, list);
      switch (res)
	{
	case ghw_res_error:
	case ghw_res_eof:
	  free_2(list);
	  return;
	case ghw_res_ok:
	case ghw_res_other:
	  break;
	case ghw_res_snapshot:
	  if (h->snap_time > GLOBALS->max_time)
	    GLOBALS->max_time = h->snap_time;
	  /* printf ("Time is "GHWPRI64"\n", h->snap_time); */

	  for (i = 0; i < h->nbr_sigs; i++)
	    add_history (h, GLOBALS->nxp_ghw_c_1[i], i);
	  break;
	case ghw_res_cycle:
	  while (1)
	    {
	      int sig;

	      /* printf ("Time is "GHWPRI64"\n", h->snap_time); */
	      if (h->snap_time < LLDescriptor(9223372036854775807))
		{
		  if (h->snap_time > GLOBALS->max_time)
		    GLOBALS->max_time = h->snap_time;

		  for (i = 0; (sig = list[i]) != 0; i++)
                    {
                      size_t nxp_idx = (size_t)sig;
                      if (nxp_idx > GLOBALS->nbr_sigs_ghw_c_1) ghw_error_exit();
                      add_history (h, GLOBALS->nxp_ghw_c_1[nxp_idx], sig);
                    }
		}
	      res = ghw_read_cycle_next (h);
	      if (res != 1)
		break;
	      res = ghw_read_cycle_cont (h, list);
	      if (res < 0)
		break;
	    }
	  if (res < 0)
	    break;
	  res = ghw_read_cycle_end (h);
	  if (res < 0)
	    break;
	  break;
	}
    }
}

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

TimeType
ghw_main(char *fname)
{
  struct ghw_handler handle = {0};
  int i;
  unsigned int ui;
  int rc;

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

 handle.flag_verbose = 0;
 if ((rc=ghw_open (&handle, fname)) < 0)
   {
     fprintf (stderr, "Error opening ghw file '%s', rc=%d.\n", fname, rc);
     return(LLDescriptor(0));        /* look at return code in caller for success status... */
   }

 GLOBALS->time_scale = 1;
 GLOBALS->time_dimension = 'f';
 GLOBALS->asbuf = malloc_2(4097);

 if (ghw_read_base (&handle) < 0)
   {
     free_2(GLOBALS->asbuf);
     GLOBALS->asbuf = NULL;
     fprintf (stderr, "Error in ghw file '%s'.\n", fname);
     return(LLDescriptor(0));        /* look at return code in caller for success status... */
   }

 if (handle.hie == NULL)
   {
     free_2(GLOBALS->asbuf);
     GLOBALS->asbuf = NULL;
     fprintf (stderr, "Error in ghw file '%s': NO HIE.\n", fname);
     return(LLDescriptor(0));        /* look at return code in caller for success status... */
   }

 GLOBALS->min_time = 0;
 GLOBALS->max_time = 0;

 GLOBALS->nbr_sig_ref_ghw_c_1 = 0;

 GLOBALS->nbr_sigs_ghw_c_1 = handle.nbr_sigs;
 GLOBALS->nxp_ghw_c_1 =(struct Node **)calloc_2(handle.nbr_sigs, sizeof(struct Node *));
 for(ui=0;ui<handle.nbr_sigs;ui++)
	{
        GLOBALS->nxp_ghw_c_1[ui] = (struct Node *)calloc_2(1,sizeof(struct Node));
	}

 GLOBALS->treeroot = build_hierarchy (&handle, handle.hie);
 /* GHW does not contains a 'top' name.
    FIXME: should use basename of the file.  */

 create_facs (&handle);
 read_traces (&handle);
 add_tail (&handle);

 set_fac_name (&handle);
 free_2(GLOBALS->nxp_ghw_c_1); GLOBALS->nxp_ghw_c_1 = NULL;

 /* fix up names on aliased nodes via cloning... */
 for(i=0;i<GLOBALS->numfacs;i++)
	{
	if(strcmp(GLOBALS->facs[i]->name, GLOBALS->facs[i]->n->nname))
		{
		struct Node *n = malloc_2(sizeof(struct Node));
		memcpy(n, GLOBALS->facs[i]->n, sizeof(struct Node));
		GLOBALS->facs[i]->n = n;
		n->nname = GLOBALS->facs[i]->name;
		}
	}

 /* treeroot->name = "top"; */
 {
 const char *base_hier = "top";

 struct tree *t = calloc_2(1, sizeof(struct tree) + strlen(base_hier) + 1);
 memcpy(t, GLOBALS->treeroot, sizeof(struct tree));
 strcpy(t->name, base_hier); /* scan-build false warning here, thinks name[1] is total length */
#ifndef WAVE_TALLOC_POOL_SIZE
 free_2(GLOBALS->treeroot); /* if using tree alloc pool, can't deallocate this */
#endif
 GLOBALS->treeroot = t;
 }

 ghw_close (&handle);

 rechain_facs();	/* vectorize bitblasted nets */
 ghw_sortfacs();	/* sort nets as ghw is unsorted ... also fix hier tree (it should really be built *after* facs are sorted!) */

#if 0
 treedebug(GLOBALS->treeroot,"");
 facs_debug();
#endif

  GLOBALS->is_ghw = 1;

  fprintf(stderr, "["TTFormat"] start time.\n["TTFormat"] end time.\n", GLOBALS->min_time*GLOBALS->time_scale, GLOBALS->max_time*GLOBALS->time_scale);
  if(GLOBALS->num_glitches_ghw_c_1) fprintf(stderr, "Warning: encountered %d glitch%s across %d glitch region%s.\n",
                GLOBALS->num_glitches_ghw_c_1, (GLOBALS->num_glitches_ghw_c_1!=1)?"es":"",
                GLOBALS->num_glitch_regions_ghw_c_1, (GLOBALS->num_glitch_regions_ghw_c_1!=1)?"s":"");

  return GLOBALS->max_time;
}

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

