/* Gengtype persistent state serialization & de-serialization.
   Useful for gengtype in plugin mode.

   Copyright (C) 2010  Free Software Foundation, Inc.

   This file is part of GCC.

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

   GCC 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 COPYING3.  If not see
   <http://www.gnu.org/licenses/>.

   Contributed by Jeremie Salvucci <jeremie.salvucci@free.fr>
   and Basile Starynkevitch <basile@starynkevitch.net>
*/

#ifdef GENERATOR_FILE
#include "bconfig.h"
#else
#include "config.h"
#endif
#include "system.h"
#include "errors.h"	/* For fatal.  */
#include "double-int.h"
#include "hashtab.h"
#include "version.h"	/* For version_string & pkgversion_string.  */
#include "obstack.h"
#include "gengtype.h"



/* Gives the file location of a type, if any.  */
static inline struct fileloc*
type_lineloc (const_type_p ty)
{
  if (!ty)
    return NULL;
  switch (ty->kind)
    {
    case TYPE_NONE:
      gcc_unreachable ();
    case TYPE_STRUCT:
    case TYPE_UNION:
    case TYPE_LANG_STRUCT:
      return CONST_CAST (struct fileloc*, &ty->u.s.line);
    case TYPE_PARAM_STRUCT:
      return CONST_CAST (struct fileloc*, &ty->u.param_struct.line);
    case TYPE_SCALAR:
    case TYPE_STRING:
    case TYPE_POINTER:
    case TYPE_ARRAY:
      return NULL;
    default:
      gcc_unreachable ();
    }
}

/* The state file has simplistic lispy lexical tokens.  Its lexer gives
   a linked list of struct state_token_st, thru the peek_state_token
   function.  Lexical tokens are consumed with next_state_tokens.  */


/* The lexical kind of each lispy token.  */
enum state_token_en
{
  STOK_NONE,                    /* Never used.  */
  STOK_INTEGER,                 /* Integer token.  */
  STOK_STRING,                  /* String token.  */
  STOK_LEFTPAR,                 /* Left opening parenthesis.  */
  STOK_RIGHTPAR,                /* Right closing parenthesis.  */
  STOK_NAME                     /* hash-consed name or identifier.  */
};


/* Structure and hash-table used to share identifiers or names.  */
struct state_ident_st
{
  /* TODO: We could improve the parser by reserving identifiers for
     state keywords and adding a keyword number for them.  That would
     mean adding another field in this state_ident_st struct.  */
  char stid_name[1];		/* actually bigger & null terminated */
};
static htab_t state_ident_tab;


/* The state_token_st structure is for lexical tokens in the read
   state file.  The stok_kind field discriminates the union.  Tokens
   are allocated by peek_state_token which calls read_a_state_token
   which allocate them.  Tokens are freed by calls to
   next_state_tokens.  Token are organized in a FIFO look-ahead queue
   filled by peek_state_token.  */
struct state_token_st
{
  enum state_token_en stok_kind;	/* the lexical kind
					   discriminates the stok_un
					   union  */
  int stok_line;			/* the line number */
  int stok_col;				/* the column number */
  const char *stok_file;		/* the file path */
  struct state_token_st *stok_next;	/* the next token in the
					   queue, when peeked */
  union		                        /* discriminated by stok_kind! */
  {
    int stok_num;			/* when STOK_INTEGER */
    char stok_string[1];		/* when STOK_STRING, actual size is
					   bigger and null terminated */
    struct state_ident_st *stok_ident;	/* when STOK_IDENT */
    void *stok_ptr;		        /* null otherwise */
  }
  stok_un;
};




#define NULL_STATE_TOKEN (struct state_token_st*)0

/* the state_token pointer contains the leftmost current token.  The
   tokens are organized in a linked queue, using stok_next, for token
   look-ahead.  */
struct state_token_st *state_token = NULL_STATE_TOKEN;

/* Used by the reading lexer.  */
static FILE *state_file;
static const char *state_path = NULL;
static int state_line = 0;
static long state_bol = 0;	/* offset of beginning of line */


/* Counter of written types.  */
static int state_written_type_count = 0;


/* Fatal error messages when reading the state.  They are extremely
   unlikely, and only appear when this gengtype-state.c file is buggy,
   or when reading a gengtype state which was not generated by the
   same version of gengtype or GCC.  */


/* Fatal message while reading state.  */
static inline void 
fatal_reading_state (struct state_token_st* tok, const char*msg)
{
  if (tok)
    fatal ("%s:%d:%d: Invalid state file; %s",
	   tok->stok_file, tok->stok_line, tok->stok_col, 
	   msg); 
  else
    fatal ("%s:%d: Invalid state file; %s", 
	   state_path, state_line, msg);
}


/* Fatal printf-like message while reading state.  This can't be a
   function, because there is no way to pass a va_arg to a variant of
   fatal.  */
#define fatal_reading_state_printf(Tok,Fmt,...) do {	\
    struct state_token_st* badtok = Tok;		\
    if (badtok)						\
      fatal ("%s:%d:%d: Invalid state file; " Fmt,	\
	      badtok->stok_file,			\
	      badtok->stok_line,			\
	      badtok->stok_col, __VA_ARGS__);		\
    else						\
      fatal ("%s:%d: Invalid state file; " Fmt,		\
	     state_path, state_line, __VA_ARGS__);	\
  } while(0)


/* Find or allocate an identifier in our name hash table.  */
static struct state_ident_st *
state_ident_by_name (const char *name, enum insert_option optins)
{
  PTR *slot = NULL;
  int namlen = 0;
  struct state_ident_st *stid = NULL;

  if (!name || !name[0])
    return NULL;

  slot = htab_find_slot (state_ident_tab, name, optins);
  if (!slot)
    return NULL;

  namlen = strlen (name);
  stid =
    (struct state_ident_st *) xmalloc (sizeof (struct state_ident_st) +
				       namlen);
  memset (stid, 0, sizeof (struct state_ident_st) + namlen);
  strcpy (stid->stid_name, name);
  *slot = stid;

  return stid;
}

/* Our token lexer is heavily inspired by MELT's lexer, and share some
   code with the file gcc/melt-runtime.c of the GCC MELT branch!  We
   really want the gengtype state to be easily parsable by MELT.  This
   is a usual lispy lexing routine, dealing with spaces and comments,
   numbers, parenthesis, names, strings.  */
static struct state_token_st *
read_a_state_token (void)
{
  int c = 0;
  long curoff = 0;
  struct state_token_st *tk = NULL;

 again: /* Read again, e.g. after a comment or spaces.  */
  c = getc (state_file);
  if (c == EOF)
    return NULL;

  /* Handle spaces, count lines.  */
  if (c == '\n')
    {
      state_line++;
      state_bol = curoff = ftell (state_file);
      goto again;
    };
  if (ISSPACE (c))
    goto again;
  /* Skip comments starting with semi-colon.  */
  if (c == ';')
    {	
      do
	{
	  c = getc (state_file);
	}
      while (c > 0 && c != '\n');
      if (c == '\n')
	{
	  state_line++;
	  state_bol = curoff = ftell (state_file);
	}
      goto again;
    };
  /* Read signed numbers.  */
  if (ISDIGIT (c) || c == '-' || c == '+')
    {				/* number */
      int n = 0;
      ungetc (c, state_file);
      curoff = ftell (state_file);
      if (fscanf (state_file, "%d", &n) <= 0)
	fatal_reading_state (NULL_STATE_TOKEN, "Lexical error in number");
      tk = XCNEW (struct state_token_st);
      tk->stok_kind = STOK_INTEGER;
      tk->stok_line = state_line;
      tk->stok_col = curoff - state_bol;
      tk->stok_file = state_path;
      tk->stok_next = NULL;
      tk->stok_un.stok_num = n;

      return tk;
    }
  /* Read an opening left parenthesis.  */
  else if (c == '(')
    {
      curoff = ftell (state_file);
      tk = XCNEW (struct state_token_st);
      tk->stok_kind = STOK_LEFTPAR;
      tk->stok_line = state_line;
      tk->stok_col = curoff - state_bol;
      tk->stok_file = state_path;
      tk->stok_next = NULL;

      return tk;
    }
  /* Read an closing right parenthesis.  */
  else if (c == ')')
    {
      curoff = ftell (state_file);
      tk = XCNEW (struct state_token_st);
      tk->stok_kind = STOK_RIGHTPAR;
      tk->stok_line = state_line;
      tk->stok_col = curoff - state_bol;
      tk->stok_file = state_path;
      tk->stok_next = NULL;

      return tk;
    }
  /* Read identifiers, using an obstack.  */
  else if (ISALPHA (c) || c == '_' || c == '$' || c == '!' || c == '#')
    {
      struct obstack id_obstack;
      struct state_ident_st *sid = NULL;
      char *ids = NULL;
      obstack_init (&id_obstack);
      curoff = ftell (state_file);
      while (ISALNUM (c) || c == '_' || c == '$' || c == '!' || c == '#')
	{
	  obstack_1grow (&id_obstack, c);
	  c = getc (state_file);
	  if (c < 0)
	    break;
	};
      if (c >= 0)
	ungetc (c, state_file);
      obstack_1grow (&id_obstack, (char) 0);
      ids = XOBFINISH (&id_obstack, char *);
      sid = state_ident_by_name (ids, INSERT);
      obstack_free (&id_obstack, NULL);
      ids = NULL;
      tk = XCNEW (struct state_token_st);
      tk->stok_kind = STOK_NAME;
      tk->stok_line = state_line;
      tk->stok_col = curoff - state_bol;
      tk->stok_file = state_path;
      tk->stok_next = NULL;
      tk->stok_un.stok_ident = sid;

      return tk;
    }
  /* Read a string, dealing with escape sequences a la C! */
  else if (c == '"')
    {
      char *cstr = NULL;
      int cslen = 0;
      struct obstack bstring_obstack;
      obstack_init (&bstring_obstack);
      curoff = ftell (state_file);
      while ((c = getc (state_file)) != '"' && c >= 0)
	{
	  if (ISPRINT (c) && c != '\\')
	    obstack_1grow (&bstring_obstack, (char) c);
	  else if (ISSPACE (c) && c != '\n')
	    obstack_1grow (&bstring_obstack, (char) c);
	  else if (c == '\\')
	    {
	      c = getc (state_file);
	      switch (c)
		{
		case 'a':
		  obstack_1grow (&bstring_obstack, '\a');
		  c = getc (state_file);
		  break;
		case 'b':
		  obstack_1grow (&bstring_obstack, '\b');
		  c = getc (state_file);
		  break;
		case 't':
		  obstack_1grow (&bstring_obstack, '\t');
		  c = getc (state_file);
		  break;
		case 'n':
		  obstack_1grow (&bstring_obstack, '\n');
		  c = getc (state_file);
		  break;
		case 'v':
		  obstack_1grow (&bstring_obstack, '\v');
		  c = getc (state_file);
		  break;
		case 'f':
		  obstack_1grow (&bstring_obstack, '\f');
		  c = getc (state_file);
		  break;
		case 'r':
		  obstack_1grow (&bstring_obstack, '\r');
		  c = getc (state_file);
		  break;
		case '"':
		  obstack_1grow (&bstring_obstack, '\"');
		  c = getc (state_file);
		  break;
		case '\\':
		  obstack_1grow (&bstring_obstack, '\\');
		  c = getc (state_file);
		  break;
		case ' ':
		  obstack_1grow (&bstring_obstack, ' ');
		  c = getc (state_file);
		  break;
		case 'x':
		  {
		    unsigned int cx = 0;
		    if (fscanf (state_file, "%02x", &cx) > 0 && cx > 0)
		      obstack_1grow (&bstring_obstack, cx);
		    else
		      fatal_reading_state
			(NULL_STATE_TOKEN,
			 "Lexical error in string hex escape");
		    c = getc (state_file);
		    break;
		  }
		default:
		  fatal_reading_state
		    (NULL_STATE_TOKEN,
		     "Lexical error - unknown string escape");
		}
	    }
	  else
	    fatal_reading_state (NULL_STATE_TOKEN, "Lexical error...");
	};
      if (c != '"')
	fatal_reading_state (NULL_STATE_TOKEN, "Unterminated string");
      obstack_1grow (&bstring_obstack, '\0');
      cstr = XOBFINISH (&bstring_obstack, char *);
      cslen = strlen (cstr);
      tk = (struct state_token_st *)
	xcalloc (sizeof (struct state_token_st) + cslen, 1);
      tk->stok_kind = STOK_STRING;
      tk->stok_line = state_line;
      tk->stok_col = curoff - state_bol;
      tk->stok_file = state_path;
      tk->stok_next = NULL;
      strcpy (tk->stok_un.stok_string, cstr);
      obstack_free (&bstring_obstack, NULL);

      return tk;
    }
  /* Got an unexpected character.  */
  fatal_reading_state_printf
    (NULL_STATE_TOKEN,
     "Lexical error at offset %ld - bad character \\%03o = '%c'",
     ftell (state_file), c, c);
}

/* Used for lexical look-ahead.  Retrieves the lexical token of rank
   DEPTH, starting with 0 when reading the state file.  Gives null on
   end of file.  */
static struct state_token_st *
peek_state_token (int depth)
{
  int remdepth = depth;
  struct state_token_st **ptoken = &state_token;
  struct state_token_st *tok = NULL;

  while (remdepth >= 0)
    {
      if (*ptoken == NULL)
	{
	  *ptoken = tok = read_a_state_token ();
	  if (tok == NULL)
	    return NULL;
	}
      tok = *ptoken;
      ptoken = &((*ptoken)->stok_next);
      remdepth--;
    }

  return tok;
}

/* Consume the next DEPTH tokens and free them.  */
static void
next_state_tokens (int depth)
{
  struct state_token_st *n;

  while (depth > 0)
    {
      if (state_token != NULL)
	{
	  n = state_token->stok_next;
	  free (state_token);
	  state_token = n;
	}
      else
	fatal_reading_state (NULL_STATE_TOKEN, "Tokens stack empty");

      depth--;
    }
}

/* Safely retrieve the lexical kind of a token.  */
static inline enum state_token_en
state_token_kind (struct state_token_st *p)
{
  if (p == NULL)
    return STOK_NONE;
  else
    return p->stok_kind;
}

/* Test if a token is a given name i.e. an identifier.  */
static inline bool
state_token_is_name (struct state_token_st *p, const char *name)
{
  if (p == NULL)
    return false;

  if (p->stok_kind != STOK_NAME)
    return false;

  return !strcmp (p->stok_un.stok_ident->stid_name, name);
}


/* Following routines are useful for serializing datas.
 *
 * We want to serialize :
 *          - typedefs list
 *          - structures list
 *          - param_structs list
 *          - variables list
 *
 * So, we have one routine for each kind of data.  The main writing
 * routine is write_state.  The main reading routine is
 * read_state.  Most writing routines write_state_FOO have a
 * corresponding reading routine read_state_FOO.  Reading is done in a
 * recursive descending way, and any read error is fatal.
 */

/* When reading the state, we need to remember the previously seen
   types by their state_number, since GTY-ed types are usually
   shared.  */
static htab_t state_seen_types;

/* Return the length of a linked list made of pairs.  */
static int pair_list_length (pair_p list);

/* Write a pair */
static void write_state_pair (pair_p);

/* return the number of pairs written.  Should match the length given
   by pair_list_length.  */
static int write_state_pair_list (pair_p list);

/* Write a type.  When a type is written, its state_number is updated,
   to ensure that a "reference" to a seen type is written on next
   occurrences.  */
static void write_state_type (type_p);

/* Write a null-terminatel string using our Lispy lexical conventions,
   similar to those of C or MELT.  */
static void write_state_a_string (const char *s);

/* Compute the length of a list of pairs, starting from the first
   one.  */
static int
pair_list_length (pair_p list)
{
  int nbpair = 0;
  pair_p l = NULL;
  for (l = list; l; l = l->next)
    nbpair++;
  return nbpair;
}

/* Write a file location.  Files relative to $(srcdir) are quite
   frequent and are handled specially.  This ensures that two gengtype
   state file-s produced by gengtype on the same GCC source tree are
   very similar and can be reasonably compared with diff, even if the
   two GCC source trees have different absolute paths.  */
static void
write_state_fileloc (struct fileloc *floc)
{

  if (floc != NULL && floc->line > 0)
    {
      const char *srcrelpath = NULL;
      gcc_assert (floc->file != NULL);
      /* Most of the files are inside $(srcdir) so it is worth to
         handle them specially.  */
      srcrelpath = get_file_srcdir_relative_path (floc->file);
      if (srcrelpath != NULL)
	{
	  fprintf (state_file, "\n(!srcfileloc ");
	  write_state_a_string (srcrelpath);
	}
      else
	{
	  fprintf (state_file, "\n(!fileloc ");
	  write_state_a_string (get_input_file_name (floc->file));
	}
      fprintf (state_file, " %d", floc->line);
      fprintf (state_file, ")\n");
    }
  else
    fprintf (state_file, "nil ");
}

/* Write a list of fields.  */
static void
write_state_fields (pair_p fields)
{
  int nbfields = pair_list_length (fields);
  int nbpairs = 0;
  fprintf (state_file, "\n(!fields %d ", nbfields);
  nbpairs = write_state_pair_list (fields);
  gcc_assert (nbpairs == nbfields);
  fprintf (state_file, ")\n");
}

/* Write a null-terminated string in our lexical convention, very
   similar to the convention of C.  */
static void
write_state_a_string (const char *s)
{
  char c;

  fputs (" \"", state_file);
  for (; *s != 0; s++)
    {
      c = *s;
      switch (c)
	{
	case '\a':
	  fputs ("\\a", state_file);
	  break;
	case '\b':
	  fputs ("\\b", state_file);
	  break;
	case '\t':
	  fputs ("\\t", state_file);
	  break;
	case '\n':
	  fputs ("\\n", state_file);
	  break;
	case '\v':
	  fputs ("\\v", state_file);
	  break;
	case '\f':
	  fputs ("\\f", state_file);
	  break;
	case '\r':
	  fputs ("\\r", state_file);
	  break;
	case '\"':
	  fputs ("\\\"", state_file);
	  break;
	case '\\':
	  fputs ("\\\\", state_file);
	  break;
	default:
	  if (ISPRINT (c))
	    putc (c, state_file);
	  else
	    fprintf (state_file, "\\x%02x", (unsigned) c);
	}
    }
  fputs ("\"", state_file);
}

/* Our option-s have three kinds, each with its writer.  */
static void
write_state_string_option (options_p current)
{
  fprintf (state_file, "string ");
  if (current->info.string != NULL)
    write_state_a_string (current->info.string);
  else
    fprintf (state_file, " nil ");
}

static void
write_state_type_option (options_p current)
{
  fprintf (state_file, "type ");
  write_state_type (current->info.type);
}

static void
write_state_nested_option (options_p current)
{
  fprintf (state_file, "nested ");
  write_state_type (current->info.nested->type);
  if (current->info.nested->convert_from != NULL)
    write_state_a_string (current->info.nested->convert_from);
  else
    fprintf (state_file, " nil ");

  if (current->info.nested->convert_to != NULL)
    write_state_a_string (current->info.nested->convert_to);
  else
    fprintf (state_file, " nil ");
}

static void
write_state_option (options_p current)
{
  fprintf (state_file, "\n(!option ");

  if (current->name != NULL)
    fprintf (state_file, "%s ", current->name);
  else
    fprintf (state_file, "nil ");

  switch (current->kind)
    {
    case OPTION_STRING:
      write_state_string_option (current);
      break;
    case OPTION_TYPE:
      write_state_type_option (current);
      break;
    case OPTION_NESTED:
      write_state_nested_option (current);
      break;
    default:
      fatal ("Option tag unknown");
    }

  fprintf (state_file, ")\n");
}



/* Write a list of GTY options.  */
static void
write_state_options (options_p opt)
{
  options_p current;

  if (opt == NULL)
    {
      fprintf (state_file, "nil ");
      return;
    }

  fprintf (state_file, "\n(!options ");
  for (current = opt; current != NULL; current = current->next)
      write_state_option (current);
  fprintf (state_file, ")\n");
}


/* Write a bitmap representing a set of GCC front-end languages.  */
static void
write_state_lang_bitmap (lang_bitmap bitmap)
{
  fprintf (state_file, "%d ", (int) bitmap);
}

/* Write version information.  */
static void
write_state_version (const char *version)
{
  fprintf (state_file, "\n(!version ");
  write_state_a_string (version);
  fprintf (state_file, ")\n");
}

/* Common routine to write the common content of all types.  */
static void write_state_common_type_content (type_p current);

/* Write a scalar type.  We have only two of these.  */
static void
write_state_scalar_type (type_p current)
{
  if (current == &scalar_nonchar)
    fprintf (state_file, "scalar_nonchar ");
  else if (current == &scalar_char)
    fprintf (state_file, "scalar_char ");
  else
    fatal ("Unexpected type in write_state_scalar_type");

  write_state_common_type_content (current);
}

/* Write the string type.  There is only one such thing! */
static void
write_state_string_type (type_p current)
{
  if (current == &string_type)
    {
      fprintf (state_file, "string ");
      write_state_common_type_content (current);
    }
  else
    fatal ("Unexpected type in write_state_string_type");
}


/* Common code to write structure like types.  */
static void
write_state_struct_union_type (type_p current, const char *kindstr)
{
  DBGPRINTF ("%s type @ %p #%d '%s'", kindstr, (void *) current,
	     current->state_number, current->u.s.tag);
  fprintf (state_file, "%s ", kindstr);
  write_state_common_type_content (current);
  if (current->u.s.tag != NULL)
    write_state_a_string (current->u.s.tag);
  else
    fprintf (state_file, "nil");

  write_state_fileloc (type_lineloc (current));
  write_state_fields (current->u.s.fields);
  write_state_options (current->u.s.opt);
  write_state_lang_bitmap (current->u.s.bitmap);
}


/* Write a GTY struct type.  */
static void
write_state_struct_type (type_p current)
{
  write_state_struct_union_type (current, "struct");
  write_state_type (current->u.s.lang_struct);
}

/* write a GTY union type.  */
static void
write_state_union_type (type_p current)
{
  write_state_struct_union_type (current, "union");
  write_state_type (current->u.s.lang_struct);
}

/* Write a lang_struct type.  This is tricky and was painful to debug,
   we deal with the next field specifically within their lang_struct
   subfield, which points to a linked list of homonumous types.
   Change this function with extreme care, see also
   read_state_lang_struct_type.  */
static void
write_state_lang_struct_type (type_p current)
{
  int nbhomontype = 0;
  type_p hty = NULL;
  const char *homoname = 0;
  write_state_struct_union_type (current, "lang_struct");
  /* lang_struct-ures are particularily tricky, since their
     u.s.lang_struct field gives a list of homonymous struct-s or
     union-s! */
  DBGPRINTF ("lang_struct @ %p #%d", (void *) current, current->state_number);
  for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next)
    {
      nbhomontype++;
      DBGPRINTF ("homonymous #%d hty @ %p #%d '%s'", nbhomontype,
		 (void *) hty, hty->state_number, hty->u.s.tag);
      /* Every member of the homonymous list should have the same tag.  */
      gcc_assert (UNION_OR_STRUCT_P (hty));
      gcc_assert (hty->u.s.lang_struct == current);
      if (!homoname)
	homoname = hty->u.s.tag;
      gcc_assert (strcmp (homoname, hty->u.s.tag) == 0);
    }
  fprintf (state_file, "(!homotypes %d\n", nbhomontype);
  for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next)
    write_state_type (hty);
  fprintf (state_file, ")\n");
}

/* Write a parametrized structure GTY type.  */
static void
write_state_param_struct_type (type_p current)
{
  int i;

  fprintf (state_file, "param_struct ");
  write_state_common_type_content (current);
  write_state_type (current->u.param_struct.stru);
  for (i = 0; i < NUM_PARAM; i++)
    {
      if (current->u.param_struct.param[i] != NULL)
	write_state_type (current->u.param_struct.param[i]);
      else
	fprintf (state_file, "nil ");
    }
  write_state_fileloc (&current->u.param_struct.line);
}

/* Write a pointer type.  */
static void
write_state_pointer_type (type_p current)
{
  fprintf (state_file, "pointer ");
  write_state_common_type_content (current);
  write_state_type (current->u.p);
}

/* Write an array type.  */
static void
write_state_array_type (type_p current)
{
  fprintf (state_file, "array ");
  write_state_common_type_content (current);
  if (current->u.a.len != NULL)
    write_state_a_string (current->u.a.len);
  else
    fprintf (state_file, " nil");

  fprintf (state_file, " ");
  write_state_type (current->u.a.p);
}

/* Write the gc_used information.  */
static void
write_state_gc_used (enum gc_used_enum gus)
{
  switch (gus)
    {
    case GC_UNUSED:
      fprintf (state_file, " gc_unused");
      break;
    case GC_USED:
      fprintf (state_file, " gc_used");
      break;
    case GC_MAYBE_POINTED_TO:
      fprintf (state_file, " gc_maybe_pointed_to");
      break;
    case GC_POINTED_TO:
      fprintf (state_file, " gc_pointed_to");
      break;
    default:
      gcc_unreachable ();
    }
}

/* Utility routine to write the common content of all types.  Notice
   that the next field is *not* written on purpose.  */
static void
write_state_common_type_content (type_p current)
{
  fprintf (state_file, "%d ", current->state_number);
  /* We do not write the next type, because list of types are
     explicitly written.  However, lang_struct are special in that
     respect.  See function write_state_lang_struct_type for more.  */
  write_state_type (current->pointer_to);
  write_state_gc_used (current->gc_used);
}


/* The important and recursive routine writing GTY types as understood
   by gengtype.  Types which have a positive state_number have already
   been seen and written.  */
static void
write_state_type (type_p current)
{
  if (current == NULL)
    {
      fprintf (state_file, "nil ");
      return;
    }

  fprintf (state_file, "\n(!type ");

  if (current->state_number > 0)
    fprintf (state_file, "already_seen %d", current->state_number);
  else
    {
      state_written_type_count++;
      DBGPRINTF ("writing type #%d @%p old number %d", state_written_type_count,
		 (void *) current, current->state_number);
      current->state_number = state_written_type_count;
      switch (current->kind)
	{
	case TYPE_STRUCT:
	  write_state_struct_type (current);
	  break;
	case TYPE_UNION:
	  write_state_union_type (current);
	  break;
	case TYPE_POINTER:
	  write_state_pointer_type (current);
	  break;
	case TYPE_ARRAY:
	  write_state_array_type (current);
	  break;
	case TYPE_LANG_STRUCT:
	  write_state_lang_struct_type (current);
	  break;
	case TYPE_PARAM_STRUCT:
	  write_state_param_struct_type (current);
	  break;
	case TYPE_SCALAR:
	  write_state_scalar_type (current);
	  break;
	case TYPE_STRING:
	  write_state_string_type (current);
	  break;

	default:
	  fatal ("Unexpected type...");
	}
    }

  fprintf (state_file, ")\n");
}


/* Write a pair.  */
static void
write_state_pair (pair_p current)
{
  if (current == NULL)
    {
      fprintf (state_file, "nil)");
      return;
    }

  fprintf (state_file, "\n(!pair ");

  if (current->name != NULL)
    write_state_a_string (current->name);
  else
    write_state_a_string ("nil");

  write_state_type (current->type);
  write_state_fileloc (&(current->line));
  write_state_options (current->opt);

  fprintf (state_file, ")");
}

/* Write a pair list and return the number of pairs written.  */
static int
write_state_pair_list (pair_p list)
{
  int nbpair = 0;
  pair_p current;

  for (current = list; current != NULL; current = current->next)
    {
      write_state_pair (current);
      nbpair++;
    }
  return nbpair;

}

/* When writing imported linked lists, like typedefs, structures,
   param_structs, ... we count their length first and write it.  These
   eases the reading, and enables an extra verification on the number
   of actually read items.  */

/* Write our typedefs.  */
static void
write_state_typedefs (void)
{
  int nbtypedefs = pair_list_length (typedefs);
  int nbpairs = 0;
  fprintf (state_file, "\n(!typedefs %d\n", nbtypedefs);
  nbpairs = write_state_pair_list (typedefs);
  gcc_assert (nbpairs == nbtypedefs);
  fprintf (state_file, ")\n");
  if (verbosity_level >= 2)
    printf ("%s wrote %d typedefs\n", progname, nbtypedefs);
}

/* Write our structures.  */
static void
write_state_structures (void)
{
  int nbstruct = 0;
  type_p current;

  for (current = structures; current != NULL; current = current->next)
    nbstruct++;

  fprintf (state_file, "\n(!structures %d\n", nbstruct);

  for (current = structures; current != NULL; current = current->next)
    write_state_type (current);

  fprintf (state_file, ")\n");
  if (verbosity_level >= 2)
    printf ("%s wrote %d structures in state\n", progname, nbstruct);
}

/* Write our param_struct-s.  */
static void
write_state_param_structs (void)
{
  int nbparamstruct = 0;
  type_p current;

  for (current = param_structs; current != NULL; current = current->next)
    nbparamstruct++;

  fprintf (state_file, "\n(!param_structs %d\n", nbparamstruct);

  for (current = param_structs; current != NULL; current = current->next)
    write_state_type (current);

  fprintf (state_file, ")\n");
}

/* Write our variables.  */
static void
write_state_variables (void)
{
  int nbvars = pair_list_length (variables);
  int nbpairs = 0;
  fprintf (state_file, "\n(!variables %d\n", nbvars);
  nbpairs = write_state_pair_list (variables);
  gcc_assert (nbpairs == nbvars);
  fprintf (state_file, ")\n");
  if (verbosity_level >= 2)
    printf ("%s wrote %d variables.\n", progname, nbvars);
}

/* Write the source directory.  File locations within the source
   directory have been written specifically.  */
static void
write_state_srcdir (void)
{
  fprintf (state_file, "\n(!srcdir ");
  write_state_a_string (srcdir);
  fprintf (state_file, ")\n");
}

/* Count and write the list of our files.  */
static void
write_state_files_list (void)
{
  int i = 0;
  /* Write the list of files with their lang_bitmap.  */
  fprintf (state_file, "\n(!fileslist %d\n", (int) num_gt_files);
  for (i = 0; i < (int) num_gt_files; i++)
    {
      const char *cursrcrelpath = NULL;
      const input_file *curfil = gt_files[i];
      /* Most of the files are inside $(srcdir) so it is worth to
         handle them specially.  */
      cursrcrelpath = get_file_srcdir_relative_path (curfil);
      if (cursrcrelpath)
	{
	  fprintf (state_file, "(!srcfile %d ", get_lang_bitmap (curfil));
	  write_state_a_string (cursrcrelpath);
	}
      else
	{
	  fprintf (state_file, "(!file %d ", get_lang_bitmap (curfil));
	  write_state_a_string (get_input_file_name (curfil));
	}
      fprintf (state_file, ")\n");
    }
  fprintf (state_file, ")\n");
}

/* Write the list of GCC front-end languages.  */
static void
write_state_languages (void)
{
  int i = 0;
  fprintf (state_file, "\n(!languages %d", (int) num_lang_dirs);
  for (i = 0; i < (int) num_lang_dirs; i++)
    {
      /* Languages names are identifiers, we expect only letters or
         underscores or digits in them.  In particular, C++ is not a
         valid language name, but cp is valid.  */
      fprintf (state_file, " %s", lang_dir_names[i]);
    }
  fprintf (state_file, ")\n");
}

/* Write the trailer.  */
static void
write_state_trailer (void)
{
  /* This test should probably catch IO errors like disk full...  */
  if (fputs ("\n(!endfile)\n", state_file) == EOF)
    fatal ("failed to write state trailer [%s]", xstrerror (errno));
}

/* The write_state routine is the only writing routine called by main
   in gengtype.c.  To avoid messing the state if gengtype is
   interrupted or aborted, we write a temporary file and rename it
   after having written it in totality.  */
void
write_state (const char *state_path)
{
  long statelen = 0;
  time_t now = 0;
  char *temp_state_path = NULL;
  char tempsuffix[40];
  time (&now);

  /* We write a unique temporary file which is renamed when complete
   * only.  So even if gengtype is interrupted, the written state file
   * won't be partially written, since the temporary file is not yet
   * renamed in that case.  */
  memset (tempsuffix, 0, sizeof (tempsuffix));
  snprintf (tempsuffix, sizeof (tempsuffix) - 1, "-%ld-%d.tmp", (long) now,
	    (int) getpid ());
  temp_state_path = concat (state_path, tempsuffix, NULL);
  state_file = fopen (temp_state_path, "w");
  if (state_file == NULL)
    fatal ("Failed to open file %s for writing state: %s",
	   temp_state_path, xstrerror (errno));
  if (verbosity_level >= 3)
    printf ("%s writing state file %s temporarily in %s\n",
	    progname, state_path, temp_state_path);
  /* This is the first line of the state.  Perhaps the file utility
     could know about that, so don't change it often.  */
  fprintf (state_file, ";;;;@@@@ GCC gengtype state\n");
  /* Output a few comments for humans. */
  fprintf (state_file,
	   ";;; DON'T EDIT THIS FILE, since generated by GCC's gengtype\n");
  fprintf (state_file,
	   ";;; The format of this file is tied to a particular version of GCC.\n");
  fprintf (state_file,
	   ";;; Don't parse this file wihout knowing GCC gengtype internals.\n");
  fprintf (state_file,
	   ";;; This file should be parsed by the same %s which wrote it.\n",
	   progname);
  /* The first non-comment significant line gives the version string.  */
  write_state_version (version_string);
  write_state_srcdir ();
  write_state_languages ();
  write_state_files_list ();
  write_state_structures ();
  write_state_typedefs ();
  write_state_param_structs ();
  write_state_variables ();
  write_state_trailer ();
  statelen = ftell (state_file);
  if (ferror (state_file))
    fatal ("output error when writing state file %s [%s]",
	   temp_state_path, xstrerror (errno));
  if (fclose (state_file))
    fatal ("failed to close state file %s [%s]",
	   temp_state_path, xstrerror (errno));
  if (rename (temp_state_path, state_path))
    fatal ("failed to rename %s to state file %s [%s]", temp_state_path,
	   state_path, xstrerror (errno));
  free (temp_state_path);

  if (verbosity_level >= 1)
    printf ("%s wrote state file %s of %ld bytes with %d GTY-ed types\n",
	    progname, state_path, statelen, state_written_type_count);

}

/** End of writing routines!  The corresponding reading routines follow.  **/



/* Forward declarations, since some read_state_* functions are
   recursive! */
static void read_state_fileloc (struct fileloc *line);
static void read_state_options (options_p *opt);
static void read_state_type (type_p *current);
static void read_state_pair (pair_p *pair);
/* Return the number of pairs actually read.  */
static int read_state_pair_list (pair_p *list);
static void read_state_fields (pair_p *fields);
static void read_state_common_type_content (type_p current);




/* Record into the state_seen_types hash-table a type which we are
   reading, to enable recursive or circular references to it.  */
static void
record_type (type_p type)
{
  PTR *slot;

  slot = htab_find_slot (state_seen_types, type, INSERT);
  gcc_assert (slot);

  *slot = type;
}

/* Read an already seen type.  */
static void
read_state_already_seen_type (type_p *type)
{
  struct state_token_st *t0 = peek_state_token (0);

  if (state_token_kind (t0) == STOK_INTEGER)
    {
      PTR *slot = NULL;
      struct type loctype = { TYPE_SCALAR, 0, 0, 0, GC_UNUSED, {0} };

      loctype.state_number = t0->stok_un.stok_num;
      slot = htab_find_slot (state_seen_types, &loctype, NO_INSERT);
      if (slot == NULL)
	{
	  fatal_reading_state (t0, "Unknown type");
	}

      next_state_tokens (1);
      *type = (type_p) *slot;
    }
  else
    {
      fatal_reading_state (t0, "Bad seen type");
    }
}


/* Read the scalar_nonchar type.  */
static void
read_state_scalar_nonchar_type (type_p *type)
{
  *type = &scalar_nonchar;
  read_state_common_type_content (*type);
}


/* Read the scalar_char type.  */
static void
read_state_scalar_char_type (type_p *type)
{
  *type = &scalar_char;
  read_state_common_type_content (*type);
}


/* Read the string_type.  */
static void
read_state_string_type (type_p *type)
{
  *type = &string_type;
  read_state_common_type_content (*type);
}


/* Read a lang_bitmap representing a set of GCC front-end languages.  */
static void
read_state_lang_bitmap (lang_bitmap *bitmap)
{
  struct state_token_st *t;

  t = peek_state_token (0);
  if (state_token_kind (t) == STOK_INTEGER)
    {
      *bitmap = t->stok_un.stok_num;
      next_state_tokens (1);
    }
  else
    {
      fatal_reading_state (t, "Bad syntax for bitmap");
    }
}


/* Read a GTY-ed struct type.  */
static void
read_state_struct_type (type_p type)
{
  struct state_token_st *t0;

  type->kind = TYPE_STRUCT;
  read_state_common_type_content (type);
  t0 = peek_state_token (0);
  if (state_token_kind (t0) == STOK_STRING)
    {
      if (state_token_is_name (t0, "nil"))
	{
	  type->u.s.tag = NULL;
	  DBGPRINTF ("read anonymous struct type @%p #%d",
		     (void *) type, type->state_number);
	}
      else
	{
	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
	  DBGPRINTF ("read struct type @%p #%d '%s'",
		     (void *) type, type->state_number, type->u.s.tag);
	}

      next_state_tokens (1);
      read_state_fileloc (&(type->u.s.line));
      read_state_fields (&(type->u.s.fields));
      read_state_options (&(type->u.s.opt));
      read_state_lang_bitmap (&(type->u.s.bitmap));
      read_state_type (&(type->u.s.lang_struct));
    }
  else
    {
      fatal_reading_state (t0, "Bad tag in struct type");
    }
}


/* Read a GTY-ed union type.  */
static void
read_state_union_type (type_p type)
{
  struct state_token_st *t0;

  type->kind = TYPE_UNION;
  read_state_common_type_content (type);
  t0 = peek_state_token (0);
  if (state_token_kind (t0) == STOK_STRING)
    {
      if (state_token_is_name (t0, "nil"))
	{
	  type->u.s.tag = NULL;
	  DBGPRINTF ("read anonymous union type @%p #%d",
		     (void *) type, type->state_number);
	}
      else
	{
	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
	  DBGPRINTF ("read union type @%p #%d '%s'",
		     (void *) type, type->state_number, type->u.s.tag);
	}
      next_state_tokens (1);
      read_state_fileloc (&(type->u.s.line));
      read_state_fields (&(type->u.s.fields));
      read_state_options (&(type->u.s.opt));
      read_state_lang_bitmap (&(type->u.s.bitmap));
      read_state_type (&(type->u.s.lang_struct));
    }
  else
    fatal_reading_state (t0, "Bad tag in union type");
}


/* Read a GTY-ed pointer type.  */
static void
read_state_pointer_type (type_p type)
{
  type->kind = TYPE_POINTER;
  read_state_common_type_content (type);
  DBGPRINTF ("read pointer type @%p #%d", (void *) type, type->state_number);
  read_state_type (&(type->u.p));
}


/* Read a GTY-ed array type.  */
static void
read_state_array_type (type_p type)
{
  struct state_token_st *t0;

  type->kind = TYPE_ARRAY;
  read_state_common_type_content (type);
  t0 = peek_state_token (0);
  if (state_token_kind (t0) == STOK_STRING)
    {
      type->u.a.len = xstrdup (t0->stok_un.stok_string);
      DBGPRINTF ("read array type @%p #%d length '%s'",
		 (void *) type, type->state_number, type->u.a.len);
      next_state_tokens (1);
    }

  else if (state_token_is_name (t0, "nil"))
    {
      type->u.a.len = NULL;
      DBGPRINTF ("read array type @%p #%d without length",
		 (void *) type, type->state_number);
      next_state_tokens (1);
    }

  else
    fatal_reading_state (t0, "Bad array name type");
  read_state_type (&(type->u.a.p));
}



/* Read a lang_struct type for GTY-ed struct-s which depends upon GCC
   front-end languages.  This is a tricky function and it was painful
   to debug.  Change it with extreme care.  See also
   write_state_lang_struct_type.  */
static void
read_state_lang_struct_type (type_p type)
{
  struct state_token_st *t0 = NULL;
  struct state_token_st *t1 = NULL;
  struct state_token_st *t2 = NULL;

  type->kind = TYPE_LANG_STRUCT;
  read_state_common_type_content (type);
  t0 = peek_state_token (0);
  if (state_token_kind (t0) == STOK_STRING)
    {
      if (state_token_is_name (t0, "nil"))
	{
	  DBGPRINTF ("read anonymous lang_struct type @%p #%d",
		     (void *) type, type->state_number);
	  type->u.s.tag = NULL;
	}
      else
	{
	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
	  DBGPRINTF ("read lang_struct type @%p #%d '%s'",
		     (void *) type, type->state_number, type->u.s.tag);
	}
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Bad tag in lang struct type");
  read_state_fileloc (&(type->u.s.line));
  read_state_fields (&(type->u.s.fields));
  read_state_options (&(type->u.s.opt));
  read_state_lang_bitmap (&(type->u.s.bitmap));
  /* Within lang_struct-ures, the lang_struct field is a linked list
     of homonymous types! */
  t0 = peek_state_token (0);
  t1 = peek_state_token (1);
  t2 = peek_state_token (2);
  /* Parse (!homotypes <number-types> <type-1> .... <type-n>) */
  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!homotypes")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      type_p *prevty = &type->u.s.lang_struct;
      int nbhomotype = t2->stok_un.stok_num;
      int i = 0;
      t0 = t1 = t2 = NULL;
      next_state_tokens (3);
      for (i = 0; i < nbhomotype; i++)
	{
	  read_state_type (prevty);
	  t0 = peek_state_token (0);
	  if (*prevty)
	    prevty = &(*prevty)->next;
	  else
	      fatal_reading_state (t0,
				   "expecting type in homotype list for lang_struct");
	};
      if (state_token_kind (t0) != STOK_RIGHTPAR)
	fatal_reading_state (t0,
			     "expecting ) in homotype list for lang_struct");
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "expecting !homotypes for lang_struct");
}


/* Read a param_struct type for GTY parametrized structures.  */
static void
read_state_param_struct_type (type_p type)
{
  int i;
  struct state_token_st *t0;

  type->kind = TYPE_PARAM_STRUCT;
  read_state_common_type_content (type);
  DBGPRINTF ("read param_struct type @%p #%d",
	     (void *) type, type->state_number);
  read_state_type (&(type->u.param_struct.stru));

  for (i = 0; i < NUM_PARAM; i++)
    {
      t0 = peek_state_token (0);
      if (state_token_is_name (t0, "nil"))
	{
	  type->u.param_struct.param[i] = NULL;
	  next_state_tokens (1);
	}
      else
	read_state_type (&(type->u.param_struct.param[i]));
    }
  read_state_fileloc (&(type->u.param_struct.line));
}


/* Read the gc used information.  */
static void
read_state_gc_used (enum gc_used_enum *pgus)
{
  struct state_token_st *t0 = peek_state_token (0);
  if (state_token_is_name (t0, "gc_unused"))
    *pgus = GC_UNUSED;
  else if (state_token_is_name (t0, "gc_used"))
    *pgus = GC_USED;
  else if (state_token_is_name (t0, "gc_maybe_pointed_to"))
    *pgus = GC_MAYBE_POINTED_TO;
  else if (state_token_is_name (t0, "gc_pointed_to"))
    *pgus = GC_POINTED_TO;
  else
    fatal_reading_state (t0, "invalid gc_used information");
  next_state_tokens (1);
}


/* Utility function to read the common content of types.  */
static void
read_state_common_type_content (type_p current)
{
  struct state_token_st *t0 = peek_state_token (0);

  if (state_token_kind (t0) == STOK_INTEGER)
    {
      current->state_number = t0->stok_un.stok_num;
      next_state_tokens (1);
      record_type (current);
    }
  else
      fatal_reading_state_printf (t0,
				  "Expected integer for state_number line %d",
				  state_line);
  /* We don't read the next field of the type.  */
  read_state_type (&current->pointer_to);
  read_state_gc_used (&current->gc_used);
}


/* Read a GTY-ed type.  */
void
read_state_type (type_p *current)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);

  if (state_token_kind (t0) == STOK_LEFTPAR &&
      state_token_is_name (t1, "!type"))
    {
      next_state_tokens (2);
      t0 = peek_state_token (0);
      if (state_token_is_name (t0, "already_seen"))
	{
	  next_state_tokens (1);
	  read_state_already_seen_type (current);
	}
      else
	{
	  t0 = peek_state_token (0);

	  if (state_token_is_name (t0, "scalar_nonchar"))
	    {
	      next_state_tokens (1);
	      read_state_scalar_nonchar_type (current);
	    }
	  else if (state_token_is_name (t0, "scalar_char"))
	    {
	      next_state_tokens (1);
	      read_state_scalar_char_type (current);
	    }
	  else if (state_token_is_name (t0, "string"))
	    {
	      next_state_tokens (1);
	      read_state_string_type (current);
	    }
	  else if (state_token_is_name (t0, "struct"))
	    {
	      *current = XCNEW (struct type);
	      next_state_tokens (1);
	      read_state_struct_type (*current);
	    }
	  else if (state_token_is_name (t0, "union"))
	    {
	      *current = XCNEW (struct type);
	      next_state_tokens (1);
	      read_state_union_type (*current);
	    }
	  else if (state_token_is_name (t0, "lang_struct"))
	    {
	      *current = XCNEW (struct type);
	      next_state_tokens (1);
	      read_state_lang_struct_type (*current);
	    }
	  else if (state_token_is_name (t0, "param_struct"))
	    {
	      *current = XCNEW (struct type);
	      next_state_tokens (1);
	      read_state_param_struct_type (*current);
	    }
	  else if (state_token_is_name (t0, "pointer"))
	    {
	      *current = XCNEW (struct type);
	      next_state_tokens (1);
	      read_state_pointer_type (*current);
	    }
	  else if (state_token_is_name (t0, "array"))
	    {
	      *current = XCNEW (struct type);
	      next_state_tokens (1);
	      read_state_array_type (*current);
	    }
	  else
	    fatal_reading_state (t0, "bad type in (!type");
	}
      t0 = peek_state_token (0);
      if (state_token_kind (t0) != STOK_RIGHTPAR)
	fatal_reading_state (t0, "missing ) in type");
      next_state_tokens (1);
    }
  else if (state_token_is_name (t0, "nil"))
    {
      next_state_tokens (1);
      *current = NULL;
    }
  else
    fatal_reading_state (t0, "bad type syntax");
}


/* Read a file location.  Files within the source directory are dealt
   with specifically.  */
void
read_state_fileloc (struct fileloc *floc)
{
  bool issrcfile = false;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);

  gcc_assert (floc != NULL);
  gcc_assert (srcdir != NULL);

  if (state_token_kind (t0) == STOK_LEFTPAR &&
      (state_token_is_name (t1, "!fileloc")
       || (issrcfile = state_token_is_name (t1, "!srcfileloc"))))
    {
      next_state_tokens (2);
      t0 = peek_state_token (0);
      t1 = peek_state_token (1);
      if (state_token_kind (t0) == STOK_STRING &&
	  state_token_kind (t1) == STOK_INTEGER)
	{
	  char *path = t0->stok_un.stok_string;
	  if (issrcfile)
	    {
	      static const char dirsepstr[2] = { DIR_SEPARATOR, (char) 0 };
	      char *fullpath = concat (srcdir, dirsepstr, path, NULL);
	      floc->file = input_file_by_name (fullpath);
	      free (fullpath);
	    }
	  else
	    floc->file = input_file_by_name (path);
	  floc->line = t1->stok_un.stok_num;
	  next_state_tokens (2);
	}
      else
	fatal_reading_state (t0,
			     "Bad fileloc syntax, expected path string and line");
      t0 = peek_state_token (0);
      if (state_token_kind (t0) != STOK_RIGHTPAR)
	fatal_reading_state (t0, "Bad fileloc syntax, expected )");
      next_state_tokens (1);
    }
  else if (state_token_is_name (t0, "nil"))
    {
      next_state_tokens (1);
      floc->file = NULL;
      floc->line = 0;
    }
  else
    fatal_reading_state (t0, "Bad fileloc syntax");
}


/* Read the fields of a GTY-ed type.  */
void
read_state_fields (pair_p *fields)
{
  pair_p tmp = NULL;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!fields")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      int nbfields = t2->stok_un.stok_num;
      int nbpairs = 0;
      next_state_tokens (3);
      nbpairs = read_state_pair_list (&tmp);
      t0 = peek_state_token (0);
      if (nbpairs != nbfields)
	fatal_reading_state_printf
	  (t0,
	   "Mismatched fields number, expected %d got %d", nbpairs, nbfields);
      if (state_token_kind (t0) == STOK_RIGHTPAR)
	next_state_tokens (1);
      else
	fatal_reading_state (t0, "Bad fields expecting )");
    }

  *fields = tmp;
}


/* Read a string option.  */
static void
read_state_string_option (options_p opt)
{
  struct state_token_st *t0 = peek_state_token (0);
  opt->kind = OPTION_STRING;
  if (state_token_kind (t0) == STOK_STRING)
    {
      opt->info.string = xstrdup (t0->stok_un.stok_string);
      next_state_tokens (1);
    }
  else if (state_token_is_name (t0, "nil"))
    {
      opt->info.string = NULL;
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Missing name in string option");
}


/* Read a type option.  */
static void
read_state_type_option (options_p opt)
{
  opt->kind = OPTION_TYPE;
  read_state_type (&(opt->info.type));
}


/* Read a nested option.  */
static void
read_state_nested_option (options_p opt)
{
  struct state_token_st *t0;

  opt->info.nested = XCNEW (struct nested_ptr_data);
  opt->kind = OPTION_NESTED;
  read_state_type (&(opt->info.nested->type));
  t0 = peek_state_token (0);
  if (state_token_kind (t0) == STOK_STRING)
    {
      opt->info.nested->convert_from = xstrdup (t0->stok_un.stok_string);
      next_state_tokens (1);
    }
  else if (state_token_is_name (t0, "nil"))
    {
      opt->info.nested->convert_from = NULL;
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Bad nested convert_from option");

  t0 = peek_state_token (0);
  if (state_token_kind (t0) == STOK_STRING)
    {
      opt->info.nested->convert_to = xstrdup (t0->stok_un.stok_string);
      next_state_tokens (1);
    }
  else if (state_token_is_name (t0, "nil"))
    {
      opt->info.nested->convert_to = NULL;
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Bad nested convert_from option");
}


/* Read an GTY option.  */
static void
read_state_option (options_p *opt)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);

  if (state_token_kind (t0) == STOK_LEFTPAR &&
      state_token_is_name (t1, "!option"))
    {
      next_state_tokens (2);
      t0 = peek_state_token (0);
      if (state_token_kind (t0) == STOK_NAME)
	{
	  *opt = XCNEW (struct options);
	  if (state_token_is_name (t0, "nil"))
	    (*opt)->name = NULL;
	  else
	    (*opt)->name = t0->stok_un.stok_ident->stid_name;
	  next_state_tokens (1);
	  t0 = peek_state_token (0);
	  if (state_token_kind (t0) == STOK_NAME)
	    {
	      if (state_token_is_name (t0, "string"))
		{
		  next_state_tokens (1);
		  read_state_string_option (*opt);
		}
	      else if (state_token_is_name (t0, "type"))
		{
		  next_state_tokens (1);
		  read_state_type_option (*opt);
		}
	      else if (state_token_is_name (t0, "nested"))
		{
		  next_state_tokens (1);
		  read_state_nested_option (*opt);
		}
	      else
		fatal_reading_state (t0, "Bad option type");
	      t0 = peek_state_token (0);
	      if (state_token_kind (t0) != STOK_RIGHTPAR)
		fatal_reading_state (t0, "Bad syntax in option, expecting )");

	      next_state_tokens (1);
	    }
	  else
	    fatal_reading_state (t0, "Missing option type");
	}
      else
	fatal_reading_state (t0, "Bad name for option");
    }
  else
    fatal_reading_state (t0, "Bad option, waiting for )");
}

/* Read a list of options.  */
void
read_state_options (options_p *opt)
{
  options_p head = NULL;
  options_p previous = NULL;
  options_p current_option = NULL;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);

  if (state_token_kind (t0) == STOK_LEFTPAR &&
      state_token_is_name (t1, "!options"))
    {
      next_state_tokens (2);
      t0 = peek_state_token (0);
      while (state_token_kind (t0) != STOK_RIGHTPAR)
	{
	  read_state_option (&current_option);
	  if (head == NULL)
	    {
	      head = current_option;
	      previous = head;
	    }
	  else
	    {
	      previous->next = current_option;
	      previous = current_option;
	    }
	  t0 = peek_state_token (0);
	}
      next_state_tokens (1);
    }
  else if (state_token_is_name (t0, "nil"))
    {
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Bad options syntax");

  *opt = head;
}


/* Read a version, and check against the version of the gengtype.  */
static void
read_state_version (const char *version_string)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);

  if (state_token_kind (t0) == STOK_LEFTPAR &&
      state_token_is_name (t1, "!version"))
    {
      next_state_tokens (2);
      t0 = peek_state_token (0);
      t1 = peek_state_token (1);
      if (state_token_kind (t0) == STOK_STRING &&
	  state_token_kind (t1) == STOK_RIGHTPAR)
	{
	  /* Check that the read version string is the same as current
	     version.  */
	  if (strcmp (version_string, t0->stok_un.stok_string))
	    fatal_reading_state_printf (t0,
					"version string mismatch; expecting %s but got %s",
					version_string,
					t0->stok_un.stok_string);
	  next_state_tokens (2);
	}
      else
	fatal_reading_state (t0, "Missing version or right parenthesis");
    }
  else
    fatal_reading_state (t0, "Bad version syntax");
}


/* Read a pair.  */
void
read_state_pair (pair_p *current)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  if (state_token_kind (t0) == STOK_LEFTPAR &&
      state_token_is_name (t1, "!pair"))
    {
      *current = XCNEW (struct pair);
      next_state_tokens (2);
      t0 = peek_state_token (0);
      if (state_token_kind (t0) == STOK_STRING)
	{
	  if (strcmp (t0->stok_un.stok_string, "nil") == 0)
	    {
	      (*current)->name = NULL;
	    }
	  else
	    {
	      (*current)->name = xstrdup (t0->stok_un.stok_string);
	    }
	  next_state_tokens (1);
	  read_state_type (&((*current)->type));
	  read_state_fileloc (&((*current)->line));
	  read_state_options (&((*current)->opt));;
	  t0 = peek_state_token (0);
	  if (state_token_kind (t0) == STOK_RIGHTPAR)
	    {
	      next_state_tokens (1);
	    }
	  else
	    {
	      fatal_reading_state (t0, "Bad syntax for pair, )");
	    }
	}
      else
	{
	  fatal_reading_state (t0, "Bad name for pair");
	}
    }
  else if (state_token_kind (t0) == STOK_NAME &&
	   state_token_is_name (t0, "nil"))
    {
      next_state_tokens (1);
      *current = NULL;
    }
  else
    fatal_reading_state_printf (t0, "Bad syntax for pair, (!pair %d",
				state_token->stok_kind);
}


/* Return the number of pairs actually read.  */
int
read_state_pair_list (pair_p *list)
{
  int nbpair = 0;
  pair_p head = NULL;
  pair_p previous = NULL;
  pair_p tmp = NULL;
  struct state_token_st *t0 = peek_state_token (0);
  while (t0 && state_token_kind (t0) != STOK_RIGHTPAR)
    {
      read_state_pair (&tmp);
      if (head == NULL)
	{
	  head = tmp;
	  previous = head;
	}
      else
	{
	  previous->next = tmp;
	  previous = tmp;
	}
      t0 = peek_state_token (0);
      nbpair++;
    }

  /* don't consume the ); the caller will eat it.  */
  *list = head;
  return nbpair;
}

/* Read the typedefs.  */
static void
read_state_typedefs (pair_p *typedefs)
{
  int nbtypedefs = 0;
  pair_p list = NULL;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!typedefs")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      int nbpairs = 0;
      nbtypedefs = t2->stok_un.stok_num;
      next_state_tokens (3);
      nbpairs = read_state_pair_list (&list);
      t0 = peek_state_token (0);
      if (nbpairs != nbtypedefs)
	fatal_reading_state_printf
	  (t0,
	   "invalid number of typedefs, expected %d but got %d",
	   nbtypedefs, nbpairs);
      if (state_token_kind (t0) == STOK_RIGHTPAR)
	next_state_tokens (1);
      else
	fatal_reading_state (t0, "Bad typedefs syntax )");
    }
  else
    fatal_reading_state (t0, "Bad typedefs syntax (!typedefs");

  if (verbosity_level >= 2)
    printf ("%s read %d typedefs from state\n", progname, nbtypedefs);
  *typedefs = list;
}


/* Read the structures.  */
static void
read_state_structures (type_p *structures)
{
  type_p head = NULL;
  type_p previous = NULL;
  type_p tmp;
  int nbstruct = 0, countstruct = 0;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!structures")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      nbstruct = t2->stok_un.stok_num;
      next_state_tokens (3);
      t0 = peek_state_token (0);
      while (t0 && state_token_kind (t0) != STOK_RIGHTPAR)
	{
	  tmp = NULL;
	  read_state_type (&tmp);
	  countstruct++;
	  if (head == NULL)
	    {
	      head = tmp;
	      previous = head;
	    }
	  else
	    {
	      previous->next = tmp;
	      previous = tmp;
	    }
	  t0 = peek_state_token (0);
	}
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Bad structures syntax");
  if (countstruct != nbstruct)
    fatal_reading_state_printf (NULL_STATE_TOKEN, 
				"expected %d structures but got %d",
				nbstruct, countstruct);
  if (verbosity_level >= 2)
    printf ("%s read %d structures from state\n", progname, nbstruct);
  *structures = head;
}


/* Read the param_struct-s.  */
static void
read_state_param_structs (type_p *param_structs)
{
  int nbparamstructs = 0;
  int countparamstructs = 0;
  type_p head = NULL;
  type_p previous = NULL;
  type_p tmp;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!param_structs")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      nbparamstructs = t2->stok_un.stok_num;
      next_state_tokens (3);
      t0 = t1 = t2 = NULL;
      t0 = peek_state_token (0);
      while (state_token_kind (t0) != STOK_RIGHTPAR)
	{
	  tmp = NULL;
	  read_state_type (&tmp);
	  if (head == NULL)
	    {
	      head = tmp;
	      previous = head;
	    }
	  else
	    {
	      previous->next = tmp;
	      previous = tmp;
	    }
	  t0 = peek_state_token (0);
	  countparamstructs++;
	}
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "Bad param_structs syntax");
  t0 = peek_state_token (0);
  if (countparamstructs != nbparamstructs)
    fatal_reading_state_printf
      (t0,
       "invalid number of param_structs expected %d got %d",
       nbparamstructs, countparamstructs);
  *param_structs = head;
}


/* Read the variables.  */
static void
read_state_variables (pair_p *variables)
{
  pair_p list = NULL;
  int nbvars = 0;
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!variables")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      int nbpairs = 0;
      nbvars = t2->stok_un.stok_num;
      next_state_tokens (3);
      nbpairs = read_state_pair_list (&list);
      t0 = peek_state_token (0);
      if (nbpairs != nbvars)
	fatal_reading_state_printf
	  (t0, "Invalid number of variables, expected %d but got %d",
	   nbvars, nbpairs);
      if (state_token_kind (t0) == STOK_RIGHTPAR)
	next_state_tokens (1);
      else
	fatal_reading_state (t0, "Waiting for ) in variables");
    }
  else
    fatal_reading_state (t0, "Bad variables syntax");
  *variables = list;
  if (verbosity_level >= 2)
    printf ("%s read %d variables from state\n", progname, nbvars);
}


/* Read the source directory.  */
static void
read_state_srcdir (void)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  if (state_token_kind (t0) == STOK_LEFTPAR &&
      state_token_is_name (t1, "!srcdir"))
    {
      next_state_tokens (2);
      t0 = peek_state_token (0);
      t1 = peek_state_token (1);
      if (state_token_kind (t0) == STOK_STRING &&
	  state_token_kind (t1) == STOK_RIGHTPAR)
	{
	  srcdir = xstrdup (t0->stok_un.stok_string);
	  srcdir_len = strlen (srcdir);
	  next_state_tokens (2);
	  return;
	}
    }

  fatal_reading_state (t0, "Bad srcdir in state_file");
}


/* Read the sequence of GCC front-end languages.  */
static void
read_state_languages (void)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);
  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!languages")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      int i = 0;
      num_lang_dirs = t2->stok_un.stok_num;
      lang_dir_names = XCNEWVEC (const char *, num_lang_dirs);
      next_state_tokens (3);
      t0 = t1 = t2 = NULL;
      for (i = 0; i < (int) num_lang_dirs; i++)
	{
	  t0 = peek_state_token (0);
	  if (state_token_kind (t0) != STOK_NAME)
	    fatal_reading_state (t0, "expecting language name in state file");
	  lang_dir_names[i] = t0->stok_un.stok_ident->stid_name;
	  next_state_tokens (1);
	}
      t0 = peek_state_token (0);
      if (state_token_kind (t0) != STOK_RIGHTPAR)
	fatal_reading_state (t0, "missing ) in languages list of state file");
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "expecting languages list in state file");

}

/* Read the sequence of files.  */
static void
read_state_files_list (void)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!fileslist")
      && state_token_kind (t2) == STOK_INTEGER)
    {
      int i = 0;
      num_gt_files = t2->stok_un.stok_num;
      next_state_tokens (3);
      t0 = t1 = t2 = NULL;
      gt_files = XCNEWVEC (const input_file *, num_gt_files);
      for (i = 0; i < (int) num_gt_files; i++)
	{
	  bool issrcfile = FALSE;
	  t0 = t1 = t2 = NULL;
	  t0 = peek_state_token (0);
	  t1 = peek_state_token (1);
	  t2 = peek_state_token (2);
	  if (state_token_kind (t0) == STOK_LEFTPAR
	      && (state_token_is_name (t1, "!file")
		  || (issrcfile = state_token_is_name (t1, "!srcfile")))
	      && state_token_kind (t2) == STOK_INTEGER)
	    {
	      lang_bitmap bmap = t2->stok_un.stok_num;
	      next_state_tokens (3);
	      t0 = t1 = t2 = NULL;
	      t0 = peek_state_token (0);
	      t1 = peek_state_token (1);
	      if (state_token_kind (t0) == STOK_STRING
		  && state_token_kind (t1) == STOK_RIGHTPAR)
		{
		  const char *fnam = t0->stok_un.stok_string;
		  /* Allocate & fill a gt_file entry with space for the lang_bitmap before! */
		  input_file *curgt = NULL;
		  if (issrcfile)
		    {
		      static const char dirsepstr[2] =
			{ DIR_SEPARATOR, (char) 0 };
		      char *fullpath = concat (srcdir, dirsepstr, fnam, NULL);
		      curgt = input_file_by_name (fullpath);
		      free (fullpath);
		    }
		  else
		    curgt = input_file_by_name (fnam);
		  set_lang_bitmap (curgt, bmap);
		  gt_files[i] = curgt;
		  next_state_tokens (2);
		}
	      else
		fatal_reading_state (t0,
				     "bad file in !fileslist of state file");
	    }
	  else
	    fatal_reading_state (t0,
				 "expecting file in !fileslist of state file");
	};
      t0 = peek_state_token (0);
      if (!state_token_kind (t0) == STOK_RIGHTPAR)
	fatal_reading_state (t0, "missing ) for !fileslist in state file");
      next_state_tokens (1);
    }
  else
    fatal_reading_state (t0, "missing !fileslist in state file");
}


/* Read the trailer.  */
static void
read_state_trailer (void)
{
  struct state_token_st *t0 = peek_state_token (0);
  struct state_token_st *t1 = peek_state_token (1);
  struct state_token_st *t2 = peek_state_token (2);

  if (state_token_kind (t0) == STOK_LEFTPAR
      && state_token_is_name (t1, "!endfile")
      && state_token_kind (t2) == STOK_RIGHTPAR)
    next_state_tokens (3);
  else
    fatal_reading_state (t0, "missing !endfile in state file");
}


/* Utility functions for the state_seen_types hash table.  */
static unsigned
hash_type_number (const void *ty)
{
  const struct type *type = (const struct type *) ty;

  return type->state_number;
}

static int
equals_type_number (const void *ty1, const void *ty2)
{
  const struct type *type1 = (const struct type *) ty1;
  const struct type *type2 = (const struct type *) ty2;

  return type1->state_number == type2->state_number;
}

static int
string_eq (const void *a, const void *b)
{
  const char *a0 = (const char *)a;
  const char *b0 = (const char *)b;

  return (strcmp (a0, b0) == 0);
}


/* The function reading the state, called by main from gengtype.c.  */
void
read_state (const char *path)
{
  state_file = fopen (path, "r");
  if (state_file == NULL)
    fatal ("Failed to open state file %s for reading [%s]", path,
	   xstrerror (errno));
  state_path = path;
  state_line = 1;

  if (verbosity_level >= 1)
    {
      printf ("%s reading state file %s;", progname, state_path);
      if (verbosity_level >= 2)
	putchar ('\n');
      fflush (stdout);
    }

  state_seen_types =
    htab_create (2017, hash_type_number, equals_type_number, NULL);
  state_ident_tab =
    htab_create (4027, htab_hash_string, string_eq, NULL);
  read_state_version (version_string);
  read_state_srcdir ();
  read_state_languages ();
  read_state_files_list ();
  read_state_structures (&structures);
  if (ferror (state_file))
    fatal_reading_state_printf
      (NULL_STATE_TOKEN, "input error while reading state [%s]",
       xstrerror (errno));
  read_state_typedefs (&typedefs);
  read_state_param_structs (&param_structs);
  read_state_variables (&variables);
  read_state_trailer ();

  if (verbosity_level >= 1)
    {
      printf ("%s read %ld bytes.\n", progname, ftell (state_file));
      fflush (stdout);
    };

  if (fclose (state_file))
    fatal ("failed to close read state file %s [%s]",
	   path, xstrerror (errno));
  state_file = NULL;
  state_path = NULL;
}

/* End of file gengtype-state.c.  */
