/* GHDL Wavefile reader library.
  Copyright (C) 2005 Tristan Gingold

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; If not, see <gnu.org/licenses>.
*/

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

#include "libghw.h"

// 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__)

// Call malloc (size), fail exit when malloc fails (returns NULL)
void *
malloc_unwrap (size_t size)
{
  void *ret = malloc (size);
  if (ret == NULL)
  {
    fprintf(stderr, "libghw could not allocate %zu bytes. Terminating.\n", size);
    exit(12);
  }
  return ret;
}

// Call calloc ()
void *
calloc_unwrap (size_t nmemb, size_t size)
{
  void *ret = calloc(nmemb, size);
  if (ret == NULL)
  {
    fprintf(stderr, "libghw could not allocate %zu elements of size %zu bytes. Terminating.\n", nmemb, size);
    exit(12);
  }
  return ret;
}

/* Reopen H through decompressor DECOMP.  */

static int
ghw_openz (struct ghw_handler *h, const char *decomp, const char *filename)
{
  int plen = strlen (decomp) + 1 + strlen (filename) + 1;
  char *p = malloc_unwrap (plen);

  snprintf (p, plen, "%s %s", decomp, filename);
  fclose (h->stream);
  h->stream = popen (p, "r");
  free (p);

  if (h->stream == NULL)
    return -1;

  h->stream_ispipe = 1;

  return 0;
}

int
ghw_open (struct ghw_handler *h, const char *filename)
{
  char hdr[16];

  h->stream = fopen (filename, "rb");
  if (h->stream == NULL)
    return -1;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  /* Check compression layer.  */
  if (!memcmp (hdr, "\x1f\x8b", 2))
    {
      if (ghw_openz (h, "gzip -cd", filename) < 0)
	return -1;
      if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
	return -1;
    }
  else if (!memcmp (hdr, "BZ", 2))
    {
      if (ghw_openz (h, "bzip2 -cd", filename) < 0)
	return -1;
      if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
	return -1;
    }
  else
    {
      h->stream_ispipe = 0;
    }

  /* Check magic.  */
  if (memcmp (hdr, "GHDLwave\n", 9) != 0)
    return -2;
  /* Check version.  */
  if (hdr[9] != 16 || hdr[10] != 0)
    return -2;
  h->version = hdr[11];
  if (h->version > 1)
    return -3;
  if (hdr[12] == 1)
    h->word_be = 0;
  else if (hdr[12] == 2)
    h->word_be = 1;
  else
    return -4;
#if 0
  /* Endianness.  */
  {
    int endian;
    union
    {
      unsigned char b[4];
      uint32_t i;
    } v;
    v.i = 0x11223344;
    if (v.b[0] == 0x11)
      endian = 2;
    else if (v.b[0] == 0x44)
      endian = 1;
    else
      return -3;

    if (hdr[12] != 1 && hdr[12] != 2)
      return -3;
    if (hdr[12] != endian)
      h->swap_word = 1;
    else
      h->swap_word = 0;
  }
#endif
  h->word_len = hdr[13];
  h->off_len = hdr[14];

  if (hdr[15] != 0)
    return -5;

  h->hie = NULL;
  return 0;
}

int32_t
ghw_get_i32 (struct ghw_handler *h, unsigned char *b)
{
  if (h->word_be)
    return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3] << 0);
  else
    return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0] << 0);
}

// Read an i32, make sure it's positive, cast to u32
uint32_t
ghw_get_i32_positive (struct ghw_handler *h, unsigned char *b)
{
  int32_t val_i = ghw_get_i32 (h, b);
  if (val_i < 0) ghw_error_exit ();
  return (uint32_t) val_i;
}

int64_t
ghw_get_i64 (struct ghw_handler *ghw_h, unsigned char *b)
{
  int l, h;

  if (ghw_h->word_be)
    {
      h = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3] << 0);
      l = (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7] << 0);
    }
  else
    {
      l = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0] << 0);
      h = (b[7] << 24) | (b[6] << 16) | (b[5] << 8) | (b[4] << 0);
    }
  return (((int64_t) h) << 32) | l;
}

int
ghw_read_byte (struct ghw_handler *h, unsigned char *res)
{
  int v;

  v = fgetc (h->stream);
  if (v == EOF)
    return -1;
  *res = v;
  return 0;
}

int
ghw_read_uleb128 (struct ghw_handler *h, uint32_t * res)
{
  uint32_t r = 0;
  unsigned int off = 0;

  while (1)
    {
      int v = fgetc (h->stream);
      if (v == EOF)
	return -1;
      r |= (v & 0x7f) << off;
      if ((v & 0x80) == 0)
	break;
      off += 7;
    }
  *res = r;
  return 0;
}

int
ghw_read_sleb128 (struct ghw_handler *h, int32_t * res)
{
  int32_t r = 0;
  unsigned int off = 0;

  while (1)
    {
      int v = fgetc (h->stream);
      if (v == EOF)
	return -1;
      r |= ((int32_t) (v & 0x7f)) << off;
      off += 7;
      if ((v & 0x80) == 0)
	{
	  if ((v & 0x40) && off < 32)
	    r |= ~0U << off;
	  break;
	}
    }
  *res = r;
  return 0;
}

int
ghw_read_lsleb128 (struct ghw_handler *h, int64_t * res)
{
  static const int64_t r_mask = -1;
  int64_t r = 0;
  unsigned int off = 0;

  while (1)
    {
      int v = fgetc (h->stream);
      if (v == EOF)
	return -1;
      r |= ((int64_t) (v & 0x7f)) << off;
      off += 7;
      if ((v & 0x80) == 0)
	{
	  if ((v & 0x40) && off < 64)
	    r |= r_mask << off;
	  break;
	}
    }
  *res = r;
  return 0;
}

int
ghw_read_f64 (struct ghw_handler *h, double *res)
{
  /* FIXME: handle byte order.  */
  if (fread (res, sizeof (*res), 1, h->stream) != 1)
    return -1;
  return 0;
}

// Get the string with id str_id from h->str_table if the ID is valid.
static const char *
ghw_get_str(struct ghw_handler *h, uint32_t str_id)
{
  if (str_id >= h->nbr_str)
    {
      fprintf(stderr, "Invalid string ID %u in ghw file\n", str_id);
      ghw_error_exit();
    }
  return h->str_table[str_id];
}

// Read a string ID from the input file and return the correpsonding string.
const char *
ghw_read_strid (struct ghw_handler *h)
{
  uint32_t id;

  if (ghw_read_uleb128 (h, &id) != 0)
    ghw_error_exit();
  return ghw_get_str(h, id);
}

// Get the type with ID type_id from h->types if the ID is valid and
// the type has been initialized.
union ghw_type *
ghw_get_typeid (struct ghw_handler *h, uint32_t type_id)
{
  if ((type_id < 1) ||  type_id - 1 >= h->nbr_types ||
    h->types[type_id - 1] == NULL)
    {
      fprintf(stderr, "Invalid typeid ID %u in ghw file\n", type_id);
      ghw_error_exit();
    }
  return h->types[type_id - 1];
}

// Read a typeid ID from the input file and return the correpsonding string.
union ghw_type *
ghw_read_typeid (struct ghw_handler *h)
{
  uint32_t id;

  if (ghw_read_uleb128 (h, &id) != 0)
    ghw_error_exit();
  return ghw_get_typeid(h, id);
}

union ghw_range *
ghw_read_range (struct ghw_handler *h)
{
  int t = fgetc (h->stream);
  if (t == EOF)
    ghw_error_exit();
  switch (t & 0x7f)
    {
    case ghdl_rtik_type_b2:
      {
	struct ghw_range_b2 *r;
	r = malloc_unwrap (sizeof (struct ghw_range_b2));
	r->kind = t & 0x7f;
	r->dir = (t & 0x80) != 0;
	if (ghw_read_byte (h, &r->left) != 0)
	  goto err_rr_b2;
	if (ghw_read_byte (h, &r->right) != 0)
	  goto err_rr_b2;
	return (union ghw_range *) r;
      err_rr_b2:
	free (r);
	ghw_error_exit();
      }
    case ghdl_rtik_type_e8:
      {
	struct ghw_range_e8 *r;
	r = malloc_unwrap (sizeof (struct ghw_range_e8));
	r->kind = t & 0x7f;
	r->dir = (t & 0x80) != 0;
	if (ghw_read_byte (h, &r->left) != 0)
	  goto err_e8;
	if (ghw_read_byte (h, &r->right) != 0)
	  goto err_e8;
	return (union ghw_range *) r;
      err_e8:
	free (r);
	ghw_error_exit();
      }
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_p32:
      {
	struct ghw_range_i32 *r;
	r = malloc_unwrap (sizeof (struct ghw_range_i32));
	r->kind = t & 0x7f;
	r->dir = (t & 0x80) != 0;
	if (ghw_read_sleb128 (h, &r->left) != 0)
	  goto err_i32;
	if (ghw_read_sleb128 (h, &r->right) != 0)
	  goto err_i32;
	return (union ghw_range *) r;
      err_i32:
	free (r);
	ghw_error_exit();
      }
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_p64:
      {
	struct ghw_range_i64 *r;
	r = malloc_unwrap (sizeof (struct ghw_range_i64));
	r->kind = t & 0x7f;
	r->dir = (t & 0x80) != 0;
	if (ghw_read_lsleb128 (h, &r->left) != 0)
	  goto err_i64;
	if (ghw_read_lsleb128 (h, &r->right) != 0)
	  goto err_i64;
	return (union ghw_range *) r;
      err_i64:
	free (r);
	ghw_error_exit();
      }
    case ghdl_rtik_type_f64:
      {
	struct ghw_range_f64 *r;
	r = malloc_unwrap (sizeof (struct ghw_range_f64));
	r->kind = t & 0x7f;
	r->dir = (t & 0x80) != 0;
	if (ghw_read_f64 (h, &r->left) != 0)
	  goto err_f64;
	if (ghw_read_f64 (h, &r->right) != 0)
	  goto err_f64;
	return (union ghw_range *) r;
      err_f64:
	free (r);
	ghw_error_exit();
      }
    default:
      fprintf (stderr, "ghw_read_range: type %d unhandled\n", t & 0x7f);
      ghw_error_exit();
    }
}

int
ghw_read_str (struct ghw_handler *h)
{
  unsigned char hdr[12];
  unsigned i;
  char *p;
  int prev_len;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  if (hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 0 || hdr[3] != 0)
    return -1;

  // Read number of strings and total string size. Make sure that nbr_string
  // is not too large, as it's incremented afterwards.
  h->nbr_str = ghw_get_i32_positive (h, &hdr[4]);
  h->nbr_str++;
  h->str_size = (uint32_t) ghw_get_i32 (h, &hdr[8]);

  // Allocate str_table, str_content. Avoid exceeding size_t limits.
  h->str_table = (char **) calloc_unwrap(h->nbr_str, sizeof (char *));

  uint64_t alloc_size = h->str_size + h->nbr_str + 1;
  if (alloc_size > SIZE_MAX) ghw_error_exit();
  h->str_content = (char *) malloc_unwrap (alloc_size);
  char *p_end = h->str_content + alloc_size;

  if (h->flag_verbose)
    {
      printf ("Number of strings: %u\n", h->nbr_str - 1);
      printf ("String table size: %u\n", h->str_size);
    }

  h->str_table[0] = "<anon>";
  p = h->str_content;
  prev_len = 0;
  for (i = 1; i < h->nbr_str; i++)
    {
      int j;
      int c;
      char *prev;
      int sh;

      h->str_table[i] = p;
      prev = h->str_table[i - 1];
      for (j = 0; j < prev_len; j++)
      {
	if (p >= p_end) goto err_ghw_read_str;
	*p++ = prev[j];
      }

      while (1)
	{
	  c = fgetc (h->stream);
	  if (c == EOF)
	    return -1;
	  if ((c >= 0 && c <= 31) || (c >= 128 && c <= 159))
	    break;
	  if (p >= p_end) goto err_ghw_read_str;
	  *p++ = c;
	}

      if (p >= p_end) goto err_ghw_read_str;
      *p++ = 0;

      if (h->flag_verbose > 1)
	printf (" string %u (pl=%d): %s\n", i, prev_len, ghw_get_str(h, i));

      prev_len = c & 0x1f;
      sh = 5;
      while (c >= 128)
	{
	  c = fgetc (h->stream);
	  if (c == EOF)
	    return -1;
	  prev_len |= (c & 0x1f) << sh;
	  sh += 5;
	}
    }
  if (fread (hdr, 4, 1, h->stream) != 1)
    return -1;
  if (memcmp (hdr, "EOS", 4) != 0)
    return -1;
  return 0;

err_ghw_read_str:
  fprintf(stderr, "Invalid string entry in GHW file.\n");
  ghw_error_exit();


}

union ghw_type *
ghw_get_base_type (union ghw_type *t)
{
  switch (t->kind)
    {
    case ghdl_rtik_type_b2:
    case ghdl_rtik_type_e8:
    case ghdl_rtik_type_e32:
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_f64:
    case ghdl_rtik_type_p32:
    case ghdl_rtik_type_p64:
    case ghdl_rtik_type_array:
      return t;
    case ghdl_rtik_subtype_scalar:
      return t->ss.base;
    case ghdl_rtik_subtype_array:
      return t->sa.base;
    case ghdl_rtik_subtype_unbounded_array:
      return t->sua.base;
    default:
      fprintf (stderr, "ghw_get_base_type: cannot handle type %d\n", t->kind);
      ghw_error_exit();
    }
}

/* Return -1 for unbounded types.  */
static int
get_nbr_elements (union ghw_type *t)
{
  switch (t->kind)
    {
    case ghdl_rtik_type_b2:
    case ghdl_rtik_type_e8:
    case ghdl_rtik_type_e32:
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_f64:
    case ghdl_rtik_type_p32:
    case ghdl_rtik_type_p64:
    case ghdl_rtik_subtype_scalar:
      return 1;
    case ghdl_rtik_type_array:
      return -1;
    case ghdl_rtik_subtype_array:
      return t->sa.nbr_scalars;
    case ghdl_rtik_type_record:
      return t->rec.nbr_scalars;
    case ghdl_rtik_subtype_record:
      return t->sr.nbr_scalars;
    case ghdl_rtik_subtype_unbounded_record:
    case ghdl_rtik_subtype_unbounded_array:
      return -1;
    default:
      fprintf (stderr, "get_nbr_elements: unhandled type %d\n", t->kind);
      ghw_error_exit();
    }
}

int
ghw_get_range_length (union ghw_range *rng)
{
  int res;

  if (rng == NULL) ghw_error_exit();

  assert (rng != NULL);

  switch (rng->kind)
    {
    case ghdl_rtik_type_i32:
      if (rng->i32.dir)
	res = rng->i32.left - rng->i32.right + 1;
      else
	res = rng->i32.right - rng->i32.left + 1;
      break;
    case ghdl_rtik_type_b2:
      if (rng->b2.dir)
	res = rng->b2.left - rng->b2.right + 1;
      else
	res = rng->b2.right - rng->b2.left + 1;
      break;
    case ghdl_rtik_type_e8:
      if (rng->e8.dir)
	res = rng->e8.left - rng->e8.right + 1;
      else
	res = rng->e8.right - rng->e8.left + 1;
      break;
    default:
      fprintf (stderr, "get_range_length: unhandled kind %d\n", rng->kind);
      ghw_error_exit();
    }
  /* The length of a null range is 0.  */
  return (res <= 0) ? 0 : res;
}

static union ghw_type *ghw_read_type_bounds (struct ghw_handler *h,
					     union ghw_type *base);

/* Create an array subtype using BASE and ranges read from H.  */

struct ghw_subtype_array *
ghw_read_array_subtype (struct ghw_handler *h, union ghw_type *base)
{
  struct ghw_type_array *arr =
    (struct ghw_type_array *) ghw_get_base_type (base);
  if (arr->kind != ghdl_rtik_type_array) ghw_error_exit();

  struct ghw_subtype_array *sa;
  unsigned j;
  int nbr_scalars;
  int nbr_els;

  sa = malloc_unwrap (sizeof (struct ghw_subtype_array));
  sa->kind = ghdl_rtik_subtype_array;
  sa->name = NULL;
  sa->base = base;
  nbr_els = get_nbr_elements (arr->el);
  nbr_scalars = 1;
  sa->rngs = calloc_unwrap (arr->nbr_dim, sizeof (union ghw_range *));
  for (j = 0; j < arr->nbr_dim; j++)
    {
      sa->rngs[j] = ghw_read_range (h);
      nbr_scalars *= ghw_get_range_length (sa->rngs[j]);
    }
  if (nbr_els >= 0)
    {
      /* Element type is bounded.  */
      sa->el = arr->el;
    }
  else
    {
      /* Read bounds for the elements.  */
      sa->el = ghw_read_type_bounds (h, arr->el);
      nbr_els = get_nbr_elements (sa->el);
    }
  sa->nbr_scalars = nbr_scalars * nbr_els;
  return sa;
}

struct ghw_subtype_record *
ghw_read_record_subtype (struct ghw_handler *h, struct ghw_type_record *base)
{
  struct ghw_subtype_record *sr;

  sr = malloc_unwrap (sizeof (struct ghw_subtype_record));
  sr->kind = ghdl_rtik_subtype_record;
  sr->name = NULL;
  sr->base = base;
  if (base->nbr_scalars >= 0)
    {
      /* Record base type is bounded.  */
      sr->nbr_scalars = base->nbr_scalars;
      sr->els = base->els;
    }
  else
    {
      /* Read subtypes.  */
      unsigned j;
      int nbr_scalars;

      sr->els =
	calloc_unwrap (base->nbr_fields, sizeof (struct ghw_record_element));
      nbr_scalars = 0;
      for (j = 0; j < base->nbr_fields; j++)
	{
	  union ghw_type *btype = base->els[j].type;
	  int el_nbr_scalars = get_nbr_elements (btype);

	  sr->els[j].name = base->els[j].name;
	  if (el_nbr_scalars >= 0)
	    {
	      /* Element is constrained.  */
	      sr->els[j].type = btype;
	    }
	  else
	    {
	      sr->els[j].type = ghw_read_type_bounds (h, btype);
	      el_nbr_scalars = get_nbr_elements (sr->els[j].type);
	    }
	  nbr_scalars += el_nbr_scalars;
	}
      sr->nbr_scalars = nbr_scalars;
    }
  return sr;
}

/* Read bounds for BASE and create a subtype.  */

static union ghw_type *
ghw_read_type_bounds (struct ghw_handler *h, union ghw_type *base)
{
  switch (base->kind)
    {
    case ghdl_rtik_type_array:
    case ghdl_rtik_subtype_unbounded_array:
      return (union ghw_type *) ghw_read_array_subtype (h, base);
      break;
    case ghdl_rtik_type_record:
    case ghdl_rtik_subtype_unbounded_record:
      return (union ghw_type *) ghw_read_record_subtype (h, &base->rec);
      break;
    default:
      fprintf (stderr, "ghw_read_type_bounds: unhandled kind %d\n",
	       base->kind);
      ghw_error_exit();
    }
}

int
ghw_read_type (struct ghw_handler *h)
{
  unsigned char hdr[8];
  uint32_t i;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  if (hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 0 || hdr[3] != 0)
    return -1;

  h->nbr_types = ghw_get_i32_positive (h, &hdr[4]);

  // Allocate type storage, initialize it to 0 to catch invalid accesses.
  h->types = (union ghw_type **) calloc_unwrap (h->nbr_types, sizeof (union ghw_type *));

  for (i = 0; i < h->nbr_types; i++)
    {
      int t;

      t = fgetc (h->stream);
      if (t == EOF)
	return -1;
      if (h->flag_verbose > 1)
	printf ("type[%u]= %d\n", i, t);
      switch (t)
	{
	case ghdl_rtik_type_b2:
	case ghdl_rtik_type_e8:
	  {
	    struct ghw_type_enum *e;
	    uint32_t j;

	    e = malloc_unwrap (sizeof (struct ghw_type_enum));
	    e->lits = NULL;
	    e->kind = t;
	    e->wkt = ghw_wkt_unknown;
	    e->name = ghw_read_strid (h);
	    if (ghw_read_uleb128 (h, &e->nbr) != 0)
	      goto err_rt_b2;
	    uint64_t lit_size = e->nbr * sizeof (char *);
	    if (lit_size > SIZE_MAX) ghw_error_exit();
	    e->lits = (const char **) malloc_unwrap (lit_size);
	    if (h->flag_verbose > 1)
	      printf ("enum %s:", e->name);
	    for (j = 0; j < e->nbr; j++)
	      {
		e->lits[j] = ghw_read_strid (h);
		if (h->flag_verbose > 1)
		  printf (" %s", e->lits[j]);
	      }
	    if (h->flag_verbose > 1)
	      printf ("\n");
	    h->types[i] = (union ghw_type *) e;
	    break;
	  err_rt_b2:
	    free (e->lits);
	    free (e);
	    return -1;
	  }
	  break;
	case ghdl_rtik_type_i32:
	case ghdl_rtik_type_i64:
	case ghdl_rtik_type_f64:
	  {
	    struct ghw_type_scalar *sc;

	    sc = malloc_unwrap (sizeof (struct ghw_type_scalar));
	    sc->kind = t;
	    sc->name = ghw_read_strid (h);
	    if (h->flag_verbose > 1)
	      printf ("scalar: %s\n", sc->name);
	    h->types[i] = (union ghw_type *) sc;
	  } break;
	case ghdl_rtik_type_p32:
	case ghdl_rtik_type_p64:
	  {
	    struct ghw_type_physical *ph;

	    ph = malloc_unwrap (sizeof (struct ghw_type_physical));
	    ph->kind = t;
	    ph->name = ghw_read_strid (h);
	    ph->units = NULL;
	    if (h->version == 0)
	      ph->nbr_units = 0;
	    else
	      {
		unsigned j;

		if (ghw_read_uleb128 (h, &ph->nbr_units) != 0)
		  goto err_p32;
		ph->units = calloc_unwrap (ph->nbr_units, sizeof (struct ghw_unit));
		for (j = 0; j < ph->nbr_units; j++)
		  {
		    ph->units[j].name = ghw_read_strid (h);
		    if (ghw_read_lsleb128 (h, &ph->units[j].val) < 0)
		      goto err_p32;
		  }
	      }
	    if (h->flag_verbose > 1)
	      printf ("physical: %s\n", ph->name);
	    h->types[i] = (union ghw_type *) ph;
	    break;
	  err_p32:
	    free (ph->units);
	    free (ph);
	    return -1;
	  }
	  break;
	case ghdl_rtik_subtype_scalar:
	  {
	    struct ghw_subtype_scalar *ss;

	    ss = malloc_unwrap (sizeof (struct ghw_subtype_scalar));
	    ss->kind = t;
	    ss->name = ghw_read_strid (h);
	    ss->base = ghw_read_typeid (h);
	    ss->rng = ghw_read_range (h);
	    if (h->flag_verbose > 1)
	      printf ("subtype scalar: %s\n", ss->name);
	    h->types[i] = (union ghw_type *) ss;
	  } break;
	case ghdl_rtik_type_array:
	  {
	    struct ghw_type_array *arr;
	    unsigned j;

	    arr = malloc_unwrap (sizeof (struct ghw_type_array));
	    arr->dims = NULL;
	    arr->kind = t;
	    arr->name = ghw_read_strid (h);
	    arr->el = ghw_read_typeid (h);
	    if (ghw_read_uleb128 (h, &arr->nbr_dim) != 0)
	      goto err_array;
	    arr->dims =
	      (union ghw_type **) calloc_unwrap (arr->nbr_dim,
					  sizeof (union ghw_type *));
	    for (j = 0; j < arr->nbr_dim; j++)
	      arr->dims[j] = ghw_read_typeid (h);
	    if (h->flag_verbose > 1)
	      printf ("array: %s (ndim=%u) of %s\n", arr->name, arr->nbr_dim,
		      arr->el->common.name);
	    h->types[i] = (union ghw_type *) arr;
	    break;
	  err_array:
	    if (arr->dims != NULL) free (arr->dims);
	    free (arr);
	    return -1;
	  }
	  break;
	case ghdl_rtik_subtype_array:
	  {
	    struct ghw_subtype_array *sa;
	    const char *name;
	    union ghw_type *base;

	    name = ghw_read_strid (h);
	    base = ghw_read_typeid (h);

	    sa = ghw_read_array_subtype (h, base);
	    sa->name = name;
	    h->types[i] = (union ghw_type *) sa;
	    if (h->flag_verbose > 1)
	      printf ("subtype array: %s (nbr_scalars=%d)\n", sa->name,
		      sa->nbr_scalars);
	  }
	  break;
	case ghdl_rtik_subtype_unbounded_array:
	  {
	    struct ghw_subtype_unbounded_array *sua;

	    sua = malloc_unwrap (sizeof (struct ghw_subtype_unbounded_array));
	    sua->kind = t;
	    sua->name = ghw_read_strid (h);
	    sua->base = ghw_read_typeid (h);
	    h->types[i] = (union ghw_type *) sua;
	    if (h->flag_verbose > 1)
	      printf ("subtype unbounded array: %s\n", sua->name);
	  }
	  break;
	case ghdl_rtik_type_record:
	  {
	    struct ghw_type_record *rec;
	    unsigned j;
	    int nbr_scalars;

	    rec = malloc_unwrap (sizeof (struct ghw_type_record));
	    rec->kind = t;
	    rec->name = ghw_read_strid (h);
	    rec->els = NULL;
	    if (ghw_read_uleb128 (h, &rec->nbr_fields) != 0)
	      goto err_record;
	    rec->els =
	      calloc_unwrap (rec->nbr_fields, sizeof (struct ghw_record_element));
	    nbr_scalars = 0;
	    for (j = 0; j < rec->nbr_fields; j++)
	      {
		rec->els[j].name = ghw_read_strid (h);
		rec->els[j].type = ghw_read_typeid (h);
		if (nbr_scalars != -1)
		  {
		    int field_nbr_scalars =
		      get_nbr_elements (rec->els[j].type);
		    if (field_nbr_scalars == -1)
		      nbr_scalars = -1;
		    else
		      nbr_scalars += field_nbr_scalars;
		  }
	      }
	    rec->nbr_scalars = nbr_scalars;
	    if (h->flag_verbose > 1)
	      printf ("record type: %s (nbr_scalars=%d)\n", rec->name,
		      rec->nbr_scalars);
	    h->types[i] = (union ghw_type *) rec;
	    break;
	  err_record:
	    free (rec->els);
	    free (rec);
	    return -1;
	  }
	  break;
	case ghdl_rtik_subtype_record:
	  {
	    struct ghw_subtype_record *sr;
	    const char *name;
	    struct ghw_type_record *base;

	    name = ghw_read_strid (h);
	    union ghw_type * base_u = ghw_read_typeid (h);
	    if (base_u->kind != ghdl_rtik_type_record) ghw_error_exit();
	    base = (struct ghw_type_record *) base_u;

	    sr = ghw_read_record_subtype (h, base);
	    sr->name = name;
	    h->types[i] = (union ghw_type *) sr;
	    if (h->flag_verbose > 1)
	      printf ("subtype record: %s (nbr_scalars=%d)\n", sr->name,
		      sr->nbr_scalars);
	  }
	  break;
	case ghdl_rtik_subtype_unbounded_record:
	  {
	    struct ghw_subtype_unbounded_record *sur;

	    sur = malloc_unwrap (sizeof (struct ghw_subtype_unbounded_record));
	    sur->kind = t;
	    sur->name = ghw_read_strid (h);
	    union ghw_type * base_u = ghw_read_typeid (h);
	    if (base_u->kind != ghdl_rtik_type_record) ghw_error_exit();
	    sur->base = (struct ghw_type_record *) base_u;
	    h->types[i] = (union ghw_type *) sur;
	    if (h->flag_verbose > 1)
	      printf ("subtype unbounded record: %s\n", sur->name);
	  }
	  break;
	default:
	  fprintf (stderr, "ghw_read_type: unknown type %d\n", t);
	  return -1;
	}
    }
  if (fgetc (h->stream) != 0)
    return -1;
  return 0;
}

int
ghw_read_wk_types (struct ghw_handler *h)
{
  char hdr[4];

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  if (hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 0 || hdr[3] != 0)
    return -1;

  while (1)
    {
      int t;
      union ghw_type *tid;

      t = fgetc (h->stream);
      if (t == EOF)
	return -1;
      else if (t == 0)
	break;

      tid = ghw_read_typeid (h);
      if (tid->kind == ghdl_rtik_type_b2 || tid->kind == ghdl_rtik_type_e8)
	{
	  if (h->flag_verbose > 0)
	    printf ("%s: wkt=%d\n", tid->en.name, t);
	  tid->en.wkt = t;
	}
    }
  return 0;
}

void
ghw_disp_typename (struct ghw_handler *h, union ghw_type *t)
{
  (void) h;
  printf ("%s", t->common.name);
}

/* Read a signal composed of severals elements.
   Return 0 for success.  */
int
ghw_read_signal (struct ghw_handler *h, unsigned int *sigs, union ghw_type *t)
{
  switch (t->kind)
    {
    case ghdl_rtik_type_b2:
    case ghdl_rtik_type_e8:
    case ghdl_rtik_type_e32:
    case ghdl_rtik_subtype_scalar:
      {
	unsigned int sig_el;

	if (ghw_read_uleb128 (h, &sig_el) < 0)
	  return -1;
	*sigs = sig_el;
	if (sig_el == 0 || sig_el >= h->nbr_sigs)
	  return -1;
	if (h->sigs[sig_el].type == NULL)
	  h->sigs[sig_el].type = ghw_get_base_type (t);
      }
      return 0;
    case ghdl_rtik_subtype_array:
      {
	int i;
	int stride;
	int len;

	len = t->sa.nbr_scalars;
	stride = get_nbr_elements (t->sa.el);

	for (i = 0; i < len; i += stride)
	  if (ghw_read_signal (h, &sigs[i], t->sa.el) < 0)
	    return -1;
      }
      return 0;
    case ghdl_rtik_type_record:
      {
	struct ghw_type_record *r = &t->rec;
	int nbr_fields = r->nbr_fields;
	int i;
	int off;

	off = 0;
	for (i = 0; i < nbr_fields; i++)
	  {
	    if (ghw_read_signal (h, &sigs[off], r->els[i].type) < 0)
	      return -1;
	    off += get_nbr_elements (r->els[i].type);
	  }
      }
      return 0;
    case ghdl_rtik_subtype_record:
      {
	struct ghw_subtype_record *sr = &t->sr;
	int nbr_fields = sr->base->nbr_fields;
	int i;
	int off;

	off = 0;
	for (i = 0; i < nbr_fields; i++)
	  {
	    if (ghw_read_signal (h, &sigs[off], sr->els[i].type) < 0)
	      return -1;
	    off += get_nbr_elements (sr->els[i].type);
	  }
      }
      return 0;
    default:
      fprintf (stderr, "ghw_read_signal: type kind %d unhandled\n", t->kind);
      ghw_error_exit();
    }
}

int
ghw_read_value (struct ghw_handler *h,
		union ghw_val *val, union ghw_type *type)
{
  switch (ghw_get_base_type (type)->kind)
    {
    case ghdl_rtik_type_b2:
      {
	int v;
	v = fgetc (h->stream);
	if (v == EOF)
	  return -1;
	val->b2 = v;
      }
      break;
    case ghdl_rtik_type_e8:
      {
	int v;
	v = fgetc (h->stream);
	if (v == EOF)
	  return -1;
	val->e8 = v;
      }
      break;
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_p32:
      {
	int32_t v;
	if (ghw_read_sleb128 (h, &v) < 0)
	  return -1;
	val->i32 = v;
      }
      break;
    case ghdl_rtik_type_f64:
      {
	double v;
	if (ghw_read_f64 (h, &v) < 0)
	  return -1;
	val->f64 = v;
      }
      break;
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_p64:
      {
	int64_t v;
	if (ghw_read_lsleb128 (h, &v) < 0)
	  return -1;
	val->i64 = v;
      }
      break;
    default:
      fprintf (stderr, "read_value: cannot handle format %d\n", type->kind);
      ghw_error_exit();
    }
  return 0;
}

int
ghw_read_hie (struct ghw_handler *h)
{
  unsigned char hdr[16];
  int nbr_scopes;
  int nbr_sigs;
  unsigned i;
  struct ghw_hie *blk;
  struct ghw_hie **last;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  if (hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 0 || hdr[3] != 0)
    return -1;
  nbr_scopes = ghw_get_i32 (h, &hdr[4]);
  /* Number of declared signals (which may be composite).  */
  nbr_sigs = ghw_get_i32 (h, &hdr[8]);
  /* Number of basic signals.  */
  h->nbr_sigs = ghw_get_i32_positive (h, &hdr[12]);

  if (h->flag_verbose)
    printf ("%d scopes, %d signals, %u signal elements\n", nbr_scopes,
	    nbr_sigs, h->nbr_sigs);

  blk = (struct ghw_hie *) malloc_unwrap (sizeof (struct ghw_hie));
  blk->kind = ghw_hie_design;
  blk->name = NULL;
  blk->parent = NULL;
  blk->brother = NULL;
  blk->u.blk.child = NULL;

  last = &blk->u.blk.child;
  h->hie = blk;

  h->nbr_sigs++;
  h->skip_sigs = NULL;
  h->flag_full_names = 0;
  h->sigs_no_null = 0;
  h->sigs = (struct ghw_sig *) calloc_unwrap (h->nbr_sigs, sizeof (struct ghw_sig));

  while (1)
    {
      int t;
      struct ghw_hie *el;
      unsigned int str;

      t = fgetc (h->stream);
      if (t == EOF)
	return -1;
      if (t == 0)
	break;

      if (t == ghw_hie_eos)
	{
	  if (blk->parent == NULL) ghw_error_exit();
	  blk = blk->parent;
	  if (blk->u.blk.child == NULL)
	    last = &blk->u.blk.child;
	  else
	    {
	      struct ghw_hie *l = blk->u.blk.child;
	      while (l->brother != NULL)
		l = l->brother;
	      last = &l->brother;
	    }

	  continue;
	}

      el = (struct ghw_hie *) malloc_unwrap (sizeof (struct ghw_hie));
      el->kind = t;
      el->parent = blk;
      el->brother = NULL;

      /* Link.  */
      *last = el;
      last = &el->brother;

      /* Read name.  */
      if (ghw_read_uleb128 (h, &str) != 0)
	return -1;
      el->name = ghw_get_str(h, str);
      el->name = h->str_table[str];

      switch (t)
	{
	case ghw_hie_eoh:
	case ghw_hie_design:
	case ghw_hie_eos:
	  /* Should not be here.  */
	  abort();
	case ghw_hie_process:
	  el->u.blk.child = NULL;
	  break;
	case ghw_hie_block:
	case ghw_hie_generate_if:
	case ghw_hie_generate_for:
	case ghw_hie_instance:
	case ghw_hie_generic:
	case ghw_hie_package:
	  /* Create a block.  */
	  el->u.blk.child = NULL;

	  if (t == ghw_hie_generate_for)
	    {
	      el->u.blk.iter_type = ghw_read_typeid (h);
	      el->u.blk.iter_value = malloc_unwrap (sizeof (union ghw_val));
	      if (ghw_read_value
		  (h, el->u.blk.iter_value, el->u.blk.iter_type) < 0)
		return -1;
	    }
	  blk = el;
	  last = &el->u.blk.child;
	  break;
	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:
	  /* For a signal, read type.  */
	  {
	    int nbr_el;
	    unsigned int *sigs;

	    el->u.sig.type = ghw_read_typeid (h);
	    nbr_el = get_nbr_elements (el->u.sig.type);
	    if (nbr_el < 0)
	      return -1;
	    sigs =
	      (unsigned int *) calloc_unwrap (nbr_el + 1, sizeof (unsigned int));
	    el->u.sig.sigs = sigs;
	    /* Last element is NULL.  */
	    sigs[nbr_el] = 0;

	    if (h->flag_verbose > 1)
	      printf ("signal %s: %d el [", el->name, nbr_el);
	    if (ghw_read_signal (h, sigs, el->u.sig.type) < 0)
	      return -1;
	    if (h->flag_verbose > 1)
	      {
		int j;
		for (j = 0; j < nbr_el; j++)
		  printf (" #%u", sigs[j]);
		printf ("]\n");
	      }
	  }
	  break;
	default:
	  fprintf (stderr, "ghw_read_hie: unhandled kind %d\n", t);
	  ghw_error_exit();
	}
    }

  /* Allocate values. Store indication if we have NULL-type signals with index
     i > 0. Index i=0  */
  int sigs_no_null = 1;
  for (i = 0; i < h->nbr_sigs; i++)
    if (h->sigs[i].type != NULL)
      h->sigs[i].val = (union ghw_val *) malloc_unwrap (sizeof (union ghw_val));
    else if (i > 0 && sigs_no_null != 0)
      {
	printf ("Warning: ghw_read_hie: NULL type signal %ud.\n", i);
	printf ("Loading this file may take a long time.\n");
	sigs_no_null = 0;
      }

  h->sigs_no_null = sigs_no_null;
  return 0;
}

const char *
ghw_get_hie_name (struct ghw_hie *h)
{
  switch (h->kind)
    {
    case ghw_hie_eoh:
      return "eoh";
    case ghw_hie_design:
      return "design";
    case ghw_hie_block:
      return "block";
    case ghw_hie_generate_if:
      return "generate-if";
    case ghw_hie_generate_for:
      return "generate-for";
    case ghw_hie_instance:
      return "instance";
    case ghw_hie_package:
      return "package";
    case ghw_hie_process:
      return "process";
    case ghw_hie_generic:
      return "generic";
    case ghw_hie_eos:
      return "eos";
    case ghw_hie_signal:
      return "signal";
    case ghw_hie_port_in:
      return "port-in";
    case ghw_hie_port_out:
      return "port-out";
    case ghw_hie_port_inout:
      return "port-inout";
    case ghw_hie_port_buffer:
      return "port-buffer";
    case ghw_hie_port_linkage:
      return "port-linkage";
    default:
      return "??";
    }
}

void ghw_disp_value (union ghw_val *val, union ghw_type *type);

static void
print_name (struct ghw_hie *hie, int full_names)
{
  int i;
  int depth;
  struct ghw_hie *p;
  struct ghw_hie **buf;
  struct ghw_hie **end;

  /* HIE must be valid.  */
  assert (hie->name != NULL);

  if (0 == full_names)
    {
      printf (" %s: ", hie->name);
      return;
    }

  p = hie;
  depth = 0;
  while (p && p->name)
    {
      p = p->parent;
      ++depth;
    }
  buf = (struct ghw_hie **) calloc_unwrap (depth, sizeof (struct ghw_hie *));

  p = hie;
  end = depth + buf;
  while (p && p->name)
    {
      *(--end) = p;
      p = p->parent;
    }

  putchar (' ');
  putchar ('/');
  for (i = 0; i < depth; ++i)
    {
      printf ("%s%s", i ? "/" : "", buf[i]->name);
      if (ghw_hie_generate_for == buf[i]->kind)
	{
	  putchar ('(');
	  ghw_disp_value (buf[i]->u.blk.iter_value, buf[i]->u.blk.iter_type);
	  putchar (')');
	}
    }
  putchar (':');
  putchar (' ');
  free (buf);
}

void
ghw_disp_hie (struct ghw_handler *h, struct ghw_hie *top)
{
  int i;
  int indent;
  struct ghw_hie *hie;
  struct ghw_hie *n;

  hie = top;
  indent = 0;

  while (1)
    {
      if (0 == h->flag_full_names)
	for (i = 0; i < indent; i++)
	  fputc (' ', stdout);
      printf ("%s", ghw_get_hie_name (hie));

      switch (hie->kind)
	{
	case ghw_hie_design:
	case ghw_hie_block:
	case ghw_hie_generate_if:
	case ghw_hie_generate_for:
	case ghw_hie_instance:
	case ghw_hie_process:
	case ghw_hie_package:
	  if (hie->name)
	    print_name (hie, h->flag_full_names);
	  if (hie->kind == ghw_hie_generate_for)
	    {
	      printf ("(");
	      ghw_disp_value (hie->u.blk.iter_value, hie->u.blk.iter_type);
	      printf (")");
	    }
	  n = hie->u.blk.child;
	  if (n == NULL)
	    n = hie->brother;
	  else
	    indent++;
	  break;
	case ghw_hie_generic:
	case ghw_hie_eos:
	  abort();
	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 *sigs = hie->u.sig.sigs;
	    unsigned int k, num;

	    print_name (hie, h->flag_full_names);
	    ghw_disp_subtype_indication (h, hie->u.sig.type);
	    printf (":");
	    k = 0;
	    /* There can be 0-length signals.  */
	    while (sigs[k] != GHW_NO_SIG)
	      {
		/* First signal of the range.  */
		printf (" #%u", sigs[k]);
		for (num = 1; sigs[k + num] != GHW_NO_SIG; num++)
		  if (sigs[k + num] != sigs[k + num - 1] + 1)
		    break;
		if (num > 1)
		  printf ("-#%u", sigs[k + num - 1]);
		k += num;
	      }
	    n = hie->brother;
	  }
	  break;
	default:
	  abort();
	}
      printf ("\n");

      while (n == NULL)
	{
	  if (hie->parent == NULL)
	    return;
	  hie = hie->parent;
	  indent--;
	  n = hie->brother;
	}
      hie = n;
    }
}

int
ghw_read_eoh (struct ghw_handler *h)
{
  (void) h;
  return 0;
}

int
ghw_read_base (struct ghw_handler *h)
{
  unsigned char hdr[4];
  int res;

  while (1)
    {
      if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
	return -1;
      if (memcmp (hdr, "STR", 4) == 0)
	res = ghw_read_str (h);
      else if (memcmp (hdr, "HIE", 4) == 0)
	res = ghw_read_hie (h);
      else if (memcmp (hdr, "TYP", 4) == 0)
	res = ghw_read_type (h);
      else if (memcmp (hdr, "WKT", 4) == 0)
	res = ghw_read_wk_types (h);
      else if (memcmp (hdr, "EOH", 4) == 0)
	return 0;
      else
	{
	  fprintf (stderr, "ghw_read_base: unknown GHW section %c%c%c%c\n",
		   hdr[0], hdr[1], hdr[2], hdr[3]);
	  return -1;
	}
      if (res != 0)
	{
	  fprintf (stderr, "ghw_read_base: error in section %s\n", hdr);
	  return res;
	}
    }
}

int
ghw_read_signal_value (struct ghw_handler *h, struct ghw_sig *s)
{
  if (s->type == NULL) ghw_error_exit();
  return ghw_read_value (h, s->val, s->type);
}

int
ghw_read_snapshot (struct ghw_handler *h)
{
  unsigned char hdr[12];
  unsigned i;
  struct ghw_sig *s;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  if (hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 0 || hdr[3] != 0)
    return -1;
  h->snap_time = ghw_get_i64 (h, &hdr[4]);
  if (h->flag_verbose > 1)
    printf ("Time is " GHWPRI64 " fs\n", h->snap_time);

  for (i = 0; i < h->nbr_sigs; i++)
    {
      s = &h->sigs[i];
      if (s->type != NULL)
	{
	  if (h->flag_verbose > 1)
	    printf ("read type %d for sig %u\n", s->type->kind, i);
	  if (ghw_read_signal_value (h, s) < 0)
	    return -1;
	}
    }
  if (fread (hdr, 4, 1, h->stream) != 1)
    return -1;

  if (memcmp (hdr, "ESN", 4))
    return -1;

  return 0;
}

void ghw_disp_values (struct ghw_handler *h);

int
ghw_read_cycle_start (struct ghw_handler *h)
{
  unsigned char hdr[8];

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  h->snap_time = ghw_get_i64 (h, hdr);
  return 0;
}

int
ghw_read_cycle_cont (struct ghw_handler *h, int *list)
{
  uint32_t i;
  int *list_p;

  i = 0;
  list_p = list;
  while (1)
    {
      uint32_t d;

      /* Read delta to next signal.  */
      if (ghw_read_uleb128 (h, &d) < 0)
	return -1;
      if (d == 0)
	{
	  /* Last signal reached.  */
	  break;
	}

      /* Find next signal.  */
      if (h->sigs_no_null)
	{
	  /* Fast version. */
	  i = i + d;
	  if (i >= h->nbr_sigs)
	    goto err;
	}
      else
	{
	  /* Slow version: Linear search through all signals. Find d-th
	     element with non-NULL type. Note: Type of sigs[0] is ignored. */
	  while (d > 0)
	    {
	      i++;
	      if (i >= h->nbr_sigs)
		goto err;
	      if (h->sigs[i].type != NULL)
		d--;
	    }
	}

      // i=0 is not a valid signal
      if (i == 0) goto err;

      if (ghw_read_signal_value (h, &h->sigs[i]) < 0)
	return -1;
      if (list_p)
	*list_p++ = i;
    }

  if (list_p)
    *list_p = 0;
  return 0;

err:
  fprintf(stderr, "Error: ghw_read_cycle_cont: Invalid entry in GHW file.\n");
  return -1;
}

int
ghw_read_cycle_next (struct ghw_handler *h)
{
  int64_t d_time;

  if (ghw_read_lsleb128 (h, &d_time) < 0)
    return -1;
  if (d_time == -1)
    return 0;
  h->snap_time += d_time;
  return 1;
}

int
ghw_read_cycle_end (struct ghw_handler *h)
{
  char hdr[4];

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;
  if (memcmp (hdr, "ECY", 4))
    return -1;

  return 0;
}

static const char *
ghw_get_lit (union ghw_type *type, uint32_t e)
{
  if (e >= type->en.nbr)
    return "??";
  else
    return type->en.lits[e];
}

static void
ghw_disp_lit (union ghw_type *type, uint32_t e)
{
  printf ("%s (%u)", ghw_get_lit (type, e), e);
}

void
ghw_disp_value (union ghw_val *val, union ghw_type *type)
{
  switch (ghw_get_base_type (type)->kind)
    {
    case ghdl_rtik_type_b2:
      ghw_disp_lit (type, val->b2);
      break;
    case ghdl_rtik_type_e8:
      ghw_disp_lit (type, val->e8);
      break;
    case ghdl_rtik_type_i32:
      printf (GHWPRI32, val->i32);
      break;
    case ghdl_rtik_type_p64:
      printf (GHWPRI64, val->i64);
      break;
    case ghdl_rtik_type_f64:
      printf ("%g", val->f64);
      break;
    default:
      fprintf (stderr, "ghw_disp_value: cannot handle type %d\n", type->kind);
      ghw_error_exit();
    }
}

/* Put the ASCII representation of VAL into BUF, whose size if LEN.
   A NUL is always written to BUF.
*/
void
ghw_get_value (char *buf, int len, union ghw_val *val, union ghw_type *type)
{
  union ghw_type *base = ghw_get_base_type (type);

  switch (base->kind)
    {
    case ghdl_rtik_type_b2:
      if (val->b2 <= 1)
	{
	  strncpy (buf, ghw_get_lit(base, val->b2), len - 1);
	  buf[len - 1] = 0;
	}
      else
	{
	  snprintf (buf, len, "?%d", val->b2);
	}
      break;
    case ghdl_rtik_type_e8:
      if (val->b2 <= base->en.nbr)
	{
	  strncpy (buf, ghw_get_lit(base, val->e8), len - 1);
	  buf[len - 1] = 0;
	}
      else
	{
	  snprintf (buf, len, "?%d", val->e8);
	}
      break;
    case ghdl_rtik_type_i32:
      snprintf (buf, len, GHWPRI32, val->i32);
      break;
    case ghdl_rtik_type_p64:
      snprintf (buf, len, GHWPRI64, val->i64);
      break;
    case ghdl_rtik_type_f64:
      snprintf (buf, len, "%g", val->f64);
      break;
    default:
      snprintf (buf, len, "?bad type %d?", type->kind);
    }
}

static char
is_skip_signal (int *signals_to_keep, int nb_signals_to_keep, int signal)
{
  int i;
  for (i = 0; i < nb_signals_to_keep; ++i)
    {
      if (signal == signals_to_keep[i])
	{
	  return 0;
	}
    }
  return 1;
}

void
ghw_filter_signals (struct ghw_handler *h, int *signals_to_keep,
		    int nb_signals_to_keep)
{
  unsigned i;

  if (0 < nb_signals_to_keep && 0 != signals_to_keep)
    {
      if (0 == h->skip_sigs)
	{
	  h->skip_sigs = (char *) calloc_unwrap (h->nbr_sigs, sizeof (char));
	}
      for (i = 0; i < h->nbr_sigs; ++i)
	{
	  h->skip_sigs[i] =
	    is_skip_signal (signals_to_keep, nb_signals_to_keep, i);
	}
    }
  else
    {
      if (0 != h->skip_sigs)
	{
	  free (h->skip_sigs);
	  h->skip_sigs = 0;
	}
    }
}

void
ghw_disp_values (struct ghw_handler *h)
{
  unsigned i;
  for (i = 0; i < h->nbr_sigs; i++)
    {
      struct ghw_sig *s = &h->sigs[i];
      int skip = (0 != h->skip_sigs && (0 != h->skip_sigs[i]));
      if (s->type != NULL && !skip)
	{
	  printf ("#%u: ", i);
	  ghw_disp_value (s->val, s->type);
	  printf ("\n");
	}
    }
}

int
ghw_read_directory (struct ghw_handler *h)
{
  unsigned char hdr[8];
  int nbr_entries;
  int i;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  nbr_entries = ghw_get_i32 (h, &hdr[4]);

  if (h->flag_verbose)
    printf ("Directory (%d entries):\n", nbr_entries);

  for (i = 0; i < nbr_entries; i++)
    {
      unsigned char ent[8];
      int pos;

      if (fread (ent, sizeof (ent), 1, h->stream) != 1)
	return -1;

      pos = ghw_get_i32 (h, &ent[4]);
      if (h->flag_verbose)
	printf (" %s at %d\n", ent, pos);
    }

  if (fread (hdr, 4, 1, h->stream) != 1)
    return -1;
  if (memcmp (hdr, "EOD", 4))
    return -1;
  return 0;
}

int
ghw_read_tailer (struct ghw_handler *h)
{
  unsigned char hdr[8];
  int pos;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    return -1;

  pos = ghw_get_i32 (h, &hdr[4]);

  if (h->flag_verbose)
    printf ("Tailer: directory at %d\n", pos);
  return 0;
}

enum ghw_res
ghw_read_sm_hdr (struct ghw_handler *h, int *list)
{
  unsigned char hdr[4];
  int res;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    {
      if (feof (h->stream))
	return ghw_res_eof;
      else
	return ghw_res_error;
    }
  if (memcmp (hdr, "SNP", 4) == 0)
    {
      res = ghw_read_snapshot (h);
      if (res < 0)
	return res;
      return ghw_res_snapshot;
    }
  else if (memcmp (hdr, "CYC", 4) == 0)
    {
      res = ghw_read_cycle_start (h);
      if (res < 0)
	return res;
      res = ghw_read_cycle_cont (h, list);
      if (res < 0)
	return res;

      return ghw_res_cycle;
    }
  else if (memcmp (hdr, "DIR", 4) == 0)
    {
      res = ghw_read_directory (h);
    }
  else if (memcmp (hdr, "TAI", 4) == 0)
    {
      res = ghw_read_tailer (h);
    }
  else
    {
      fprintf (stderr, "unknown GHW section %c%c%c%c\n", hdr[0], hdr[1],
	       hdr[2], hdr[3]);
      return -1;
    }
  if (res != 0)
    return res;
  return ghw_res_other;
}

int
ghw_read_sm (struct ghw_handler *h, enum ghw_sm_type *sm)
{
  int res;

  while (1)
    {
      /* printf ("sm: state = %d\n", *sm); */
      switch (*sm)
	{
	case ghw_sm_init:
	case ghw_sm_sect:
	  res = ghw_read_sm_hdr (h, NULL);
	  switch (res)
	    {
	    case ghw_res_other:
	      break;
	    case ghw_res_snapshot:
	      *sm = ghw_sm_sect;
	      return res;
	    case ghw_res_cycle:
	      *sm = ghw_sm_cycle;
	      return res;
	    default:
	      return res;
	    }
	  break;
	case ghw_sm_cycle:
	  if (0)
	    printf ("Time is " GHWPRI64 " fs\n", h->snap_time);
	  if (0)
	    ghw_disp_values (h);

	  res = ghw_read_cycle_next (h);
	  if (res < 0)
	    return res;
	  if (res == 1)
	    {
	      res = ghw_read_cycle_cont (h, NULL);
	      if (res < 0)
		return res;
	      return ghw_res_cycle;
	    }
	  res = ghw_read_cycle_end (h);
	  if (res < 0)
	    return res;
	  *sm = ghw_sm_sect;
	  break;
	}
    }
}

int
ghw_read_cycle (struct ghw_handler *h)
{
  int res;

  res = ghw_read_cycle_start (h);
  if (res < 0)
    return res;
  while (1)
    {
      res = ghw_read_cycle_cont (h, NULL);
      if (res < 0)
	return res;

      if (0)
	printf ("Time is " GHWPRI64 " fs\n", h->snap_time);
      if (0)
	ghw_disp_values (h);

      res = ghw_read_cycle_next (h);
      if (res < 0)
	return res;
      if (res == 0)
	break;
    }
  res = ghw_read_cycle_end (h);
  return res;
}

int
ghw_read_dump (struct ghw_handler *h)
{
  unsigned char hdr[4];
  int res;

  while (1)
    {
      if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
	{
	  if (feof (h->stream))
	    return 0;
	  else
	    return -1;
	}
      if (memcmp (hdr, "SNP", 4) == 0)
	{
	  res = ghw_read_snapshot (h);
	  if (0 && res >= 0)
	    ghw_disp_values (h);
	}
      else if (memcmp (hdr, "CYC", 4) == 0)
	{
	  res = ghw_read_cycle (h);
	}
      else if (memcmp (hdr, "DIR", 4) == 0)
	{
	  res = ghw_read_directory (h);
	}
      else if (memcmp (hdr, "TAI", 4) == 0)
	{
	  res = ghw_read_tailer (h);
	}
      else
	{
	  fprintf (stderr, "unknown GHW section %c%c%c%c\n", hdr[0], hdr[1],
		   hdr[2], hdr[3]);
	  return -1;
	}
      if (res != 0)
	return res;
    }
}

struct ghw_section ghw_sections[] = { {"\0\0\0", NULL},
{"STR", ghw_read_str},
{"HIE", ghw_read_hie},
{"TYP", ghw_read_type},
{"WKT", ghw_read_wk_types},
{"EOH", ghw_read_eoh},
{"SNP", ghw_read_snapshot},
{"CYC", ghw_read_cycle},
{"DIR", ghw_read_directory},
{"TAI", ghw_read_tailer}
};

int
ghw_read_section (struct ghw_handler *h)
{
  unsigned char hdr[4];
  unsigned i;

  if (fread (hdr, sizeof (hdr), 1, h->stream) != 1)
    {
      if (feof (h->stream))
	return -2;
      else
	return -1;
    }

  for (i = 1; i < sizeof (ghw_sections) / sizeof (*ghw_sections); i++)
    if (memcmp (hdr, ghw_sections[i].name, 4) == 0)
      return i;

  fprintf (stderr, "ghw_read_section: unknown GHW section %c%c%c%c\n", hdr[0],
	   hdr[1], hdr[2], hdr[3]);
  return 0;
}

void
ghw_close (struct ghw_handler *h)
{
  if (h->stream)
    {
      if (h->stream_ispipe)
	pclose (h->stream);
      else
	fclose (h->stream);

      h->stream = NULL;
    }
}

const char *
ghw_get_dir (int is_downto)
{
  return is_downto ? "downto" : "to";
}

void
ghw_disp_range (union ghw_type *type, union ghw_range *rng)
{
  switch (rng->kind)
    {
    case ghdl_rtik_type_b2:
      printf ("%s %s %s", ghw_get_lit (type, rng->b2.left),
	      ghw_get_dir (rng->b2.dir), ghw_get_lit (type, rng->b2.right));
      break;
    case ghdl_rtik_type_e8:
      printf ("%s %s %s", ghw_get_lit (type, rng->e8.left),
	      ghw_get_dir (rng->e8.dir), ghw_get_lit (type, rng->e8.right));
      break;
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_p32:
      printf (GHWPRI32 " %s " GHWPRI32, rng->i32.left,
	      ghw_get_dir (rng->i32.dir), rng->i32.right);
      break;
    case ghdl_rtik_type_i64:
    case ghdl_rtik_type_p64:
      printf (GHWPRI64 " %s " GHWPRI64, rng->i64.left,
	      ghw_get_dir (rng->i64.dir), rng->i64.right);
      break;
    case ghdl_rtik_type_f64:
      printf ("%g %s %g", rng->f64.left, ghw_get_dir (rng->f64.dir),
	      rng->f64.right);
      break;
    default:
      printf ("?(%d)", rng->kind);
    }
}

static void
ghw_disp_array_subtype_bounds (struct ghw_subtype_array *a)
{
  unsigned i;
  struct ghw_type_array *base =
    (struct ghw_type_array *) ghw_get_base_type (a->base);

  printf (" (");
  for (i = 0; i < base->nbr_dim; i++)
    {
      if (i != 0)
	printf (", ");
      ghw_disp_range (base->dims[i], a->rngs[i]);
    }
  printf (")");
}

static void
ghw_disp_record_subtype_bounds (struct ghw_subtype_record *sr)
{
  struct ghw_type_record *base = sr->base;
  int is_first = 1;
  unsigned i;

  for (i = 0; i < base->nbr_fields; i++)
    {
      if (sr->els[i].type != base->els[i].type)
	{
	  if (is_first)
	    {
	      printf ("(");
	      is_first = 0;
	    }
	  else
	    printf (", ");
	  printf ("%s", base->els[i].name);
	  switch (sr->els[i].type->kind)
	    {
	    case ghdl_rtik_subtype_array:
	      ghw_disp_array_subtype_bounds (&sr->els[i].type->sa);
	      break;
	    case ghdl_rtik_subtype_record:
	      ghw_disp_record_subtype_bounds (&sr->els[i].type->sr);
	      break;
	    default:
	      printf ("??? (%d)", sr->els[i].type->kind);
	    }
	}
    }
  if (!is_first)
    printf (")");
}

static void
ghw_disp_subtype_definition (struct ghw_handler *h, union ghw_type *t)
{
  switch (t->kind)
    {
    case ghdl_rtik_subtype_scalar:
      {
	struct ghw_subtype_scalar *s = &t->ss;
	ghw_disp_typename (h, s->base);
	printf (" range ");
	ghw_disp_range (s->base, s->rng);
      } break;
    case ghdl_rtik_subtype_array:
      {
	struct ghw_subtype_array *a = &t->sa;

	ghw_disp_typename (h, (union ghw_type *) a->base);
	ghw_disp_array_subtype_bounds (a);
      } break;
    case ghdl_rtik_subtype_record:
      {
	struct ghw_subtype_record *sr = &t->sr;

	ghw_disp_typename (h, (union ghw_type *) sr->base);
	ghw_disp_record_subtype_bounds (sr);
      } break;
    case ghdl_rtik_subtype_unbounded_array:
    case ghdl_rtik_subtype_unbounded_record:
      {
	struct ghw_subtype_unbounded_record *sur = &t->sur;

	ghw_disp_typename (h, (union ghw_type *) sur->base);
      } break;
    default:
      printf ("ghw_disp_subtype_definition: unhandled type kind %d\n",
	      t->kind);
    }
}

static int
ghw_is_anonymous_type (struct ghw_handler *h, union ghw_type *t)
{
  return t->common.name == h->str_table[0];
}

void
ghw_disp_subtype_indication (struct ghw_handler *h, union ghw_type *t)
{
  if (ghw_is_anonymous_type (h, t))
    {
      /* Anonymous subtype.  */
      ghw_disp_subtype_definition (h, t);
    }
  else
    ghw_disp_typename (h, t);
}

void
ghw_disp_type (struct ghw_handler *h, union ghw_type *t)
{
  switch (t->kind)
    {
    case ghdl_rtik_type_b2:
    case ghdl_rtik_type_e8:
      {
	struct ghw_type_enum *e = &t->en;
	uint32_t i;

	printf ("type %s is (", e->name);
	for (i = 0; i < e->nbr; i++)
	  {
	    if (i != 0)
	      printf (", ");
	    printf ("%s", ghw_get_lit(t, i));
	  }
	printf (");");
	if (e->wkt != ghw_wkt_unknown)
	  printf ("  -- WKT:%d", e->wkt);
	printf ("\n");
      }
      break;
    case ghdl_rtik_type_i32:
    case ghdl_rtik_type_f64:
      {
	struct ghw_type_scalar *s = &t->sc;
	printf ("type %s is range <>;\n", s->name);
      } break;
    case ghdl_rtik_type_p32:
    case ghdl_rtik_type_p64:
      {
	unsigned i;

	struct ghw_type_physical *p = &t->ph;
	printf ("type %s is range <> units\n", p->name);
	for (i = 0; i < p->nbr_units; i++)
	  {
	    struct ghw_unit *u = &p->units[i];
	    printf ("  %s = " GHWPRI64 " %s;\n", u->name, u->val,
		    p->units[0].name);
	  }
	printf ("end units;\n");
      } break;
    case ghdl_rtik_type_array:
      {
	struct ghw_type_array *a = &t->ar;
	unsigned i;

	printf ("type %s is array (", a->name);
	for (i = 0; i < a->nbr_dim; i++)
	  {
	    if (i != 0)
	      printf (", ");
	    ghw_disp_typename (h, a->dims[i]);
	    printf (" range <>");
	  }
	printf (") of ");
	ghw_disp_subtype_indication (h, a->el);
	printf (";\n");
      }
      break;
    case ghdl_rtik_type_record:
      {
	struct ghw_type_record *r = &t->rec;
	unsigned i;

	printf ("type %s is record\n", r->name);
	for (i = 0; i < r->nbr_fields; i++)
	  {
	    printf ("  %s: ", r->els[i].name);
	    ghw_disp_subtype_indication (h, r->els[i].type);
	    printf (";\n");
	  }
	printf ("end record;\n");
      }
      break;
    case ghdl_rtik_subtype_array:
    case ghdl_rtik_subtype_scalar:
    case ghdl_rtik_subtype_record:
    case ghdl_rtik_subtype_unbounded_array:
    case ghdl_rtik_subtype_unbounded_record:
      {
	struct ghw_type_common *c = &t->common;
	printf ("subtype %s is ", c->name);
	ghw_disp_subtype_definition (h, t);
	printf (";\n");
      } break;
    default:
      printf ("ghw_disp_type: unhandled type kind %d\n", t->kind);
    }
}

void
ghw_disp_types (struct ghw_handler *h)
{
  uint32_t i;

  for (i = 0; i < h->nbr_types; i++)
    if (h->types[i] != NULL &&
	(h->flag_verbose || !ghw_is_anonymous_type (h, h->types[i])))
      ghw_disp_type (h, ghw_get_typeid(h, i+1)); // pass i+1 to access index i
}
