]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gentype-state.c: Add new file.
authorBasile Starynkevitch <basile@starynkevitch.net>
Thu, 25 Nov 2010 19:03:27 +0000 (19:03 +0000)
committerBasile Starynkevitch <bstarynk@gcc.gnu.org>
Thu, 25 Nov 2010 19:03:27 +0000 (19:03 +0000)
2010-11-25  Basile Starynkevitch  <basile@starynkevitch.net>
    Jeremie Salvucci  <jeremie.salvucci@free.fr>

* gentype-state.c: Add new file.

* gengtype.c (type count): New variable.
(new_structure, find_param_structure, create_pointer)
(create_array): Use it to set state_number in types.
(dump_everything): Improve comment.
(main): Call read_state and write_state. Print type_count.

* gengtype.h (type_lineloc): New function.
(read_state, write_state): New declarations.

* Makefile.in (GENGTYPE_FLAGS): New variable.
(s-gtype): Run gengtype twice and generate gtype.state.
(build/gengtype-state.o): New rule.
(build/gengtype$(build_exeext)): Link gengtype-state.o.
(mostlyclean): Update comment.  Remove gtype.state.

Co-Authored-By: Jeremie Salvucci <jeremie.salvucci@free.fr>
From-SVN: r167150

gcc/ChangeLog
gcc/Makefile.in
gcc/gengtype-state.c [new file with mode: 0644]
gcc/gengtype.c
gcc/gengtype.h

index d67f49993e3e220916154afa46c7976e24bf6aba..c7b2ab4ad924b01654bb7d6b3ec2d5b8e12b9825 100644 (file)
@@ -1,3 +1,23 @@
+2010-11-25  Basile Starynkevitch  <basile@starynkevitch.net>
+           Jeremie Salvucci  <jeremie.salvucci@free.fr>
+
+       * gentype-state.c: Add new file.
+
+       * gengtype.c (type count): New variable.
+       (new_structure, find_param_structure, create_pointer)
+       (create_array): Use it to set state_number in types.
+       (dump_everything): Improve comment.
+       (main): Call read_state and write_state. Print type_count.
+
+       * gengtype.h (type_lineloc): New function.
+       (read_state, write_state): New declarations.
+
+       * Makefile.in (GENGTYPE_FLAGS): New variable.
+       (s-gtype): Run gengtype twice and generate gtype.state.
+       (build/gengtype-state.o): New rule.
+       (build/gengtype$(build_exeext)): Link gengtype-state.o.
+       (mostlyclean): Update comment.  Remove gtype.state.
+
 2010-11-25  Jakub Jelinek  <jakub@redhat.com>
 
        PR middle-end/46637
index 3165f101fff2426ce1ba8adfbae9df61c21161c4..20e01aa0b57ac5cbfc9ace4453d3c622a0a06590 100644 (file)
@@ -3819,6 +3819,9 @@ ALL_GTFILES_H := $(sort $(GTFILES_H) $(GTFILES_LANG_H))
 
 $(ALL_GTFILES_H) gtype-desc.c gtype-desc.h : s-gtype ; @true
 
+### Common flags to gengtype [e.g. -v or -B backupdir]
+GENGTYPE_FLAGS= 
+
 gtyp-input.list: s-gtyp-input ; @true
 s-gtyp-input: Makefile
        @: $(call write_entries_to_file,$(GTFILES),tmp-gi.list)
@@ -3827,7 +3830,13 @@ s-gtyp-input: Makefile
 
 s-gtype: build/gengtype$(build_exeext) $(filter-out [%], $(GTFILES)) \
         gtyp-input.list
-       $(RUN_GEN) build/gengtype$(build_exeext) -S $(srcdir) -I gtyp-input.list
+# First, parse all files and save a state file.
+       $(RUN_GEN) build/gengtype$(build_exeext) $(GENGTYPE_FLAGS) \
+                    -S $(srcdir) -I gtyp-input.list -w gtype.state
+# Second, read the state file and generate all files.  This ensure that
+# gtype.state is correctly read:
+       $(RUN_GEN) build/gengtype$(build_exeext) $(GENGTYPE_FLAGS) \
+                    -r gtype.state
        $(STAMP) s-gtype
 
 generated_files = config.h tm.h $(TM_P_H) $(TM_H) multilib.h \
@@ -3923,6 +3932,8 @@ build/gengenrtl.o : gengenrtl.c $(BCONFIG_H) $(SYSTEM_H) rtl.def
 build/gengtype-lex.o : gengtype-lex.c gengtype.h $(BCONFIG_H) $(SYSTEM_H)
 build/gengtype-parse.o : gengtype-parse.c gengtype.h $(BCONFIG_H)      \
   $(SYSTEM_H)
+build/gengtype-state.o: gengtype-state.c gengtype.h $(BCONFIG_H)       \
+  $(SYSTEM_H) errors.h
 build/gengtype.o : gengtype.c $(BCONFIG_H) $(SYSTEM_H) gengtype.h      \
   rtl.def insn-notes.def errors.h double-int.h $(HASHTAB_H)             \
   $(OBSTACK_H) $(XREGEX_H)
@@ -3968,7 +3979,7 @@ build/genautomata$(build_exeext) : BUILD_LIBS += -lm
 
 # These programs are not linked with the MD reader.
 build/gengtype$(build_exeext) : build/gengtype-lex.o build/gengtype-parse.o \
-              build/version.o
+              build/gengtype-state.o build/version.o
 
 # Rule for the generator programs:
 $(genprog:%=build/gen%$(build_exeext)): build/gen%$(build_exeext): build/gen%.o $(BUILD_LIBDEPS)
@@ -4441,9 +4452,10 @@ mostlyclean: lang.mostlyclean
        -rm -f core */core
 # Delete file generated for gengtype
        -rm -f gtyp-input.list
-# Delete files generated by gengtype.c
+# Delete files generated by gengtype
        -rm -f gtype-*
        -rm -f gt-*
+       -rm -f gtype.state
 # Delete genchecksum outputs
        -rm -f *-checksum.c
 
diff --git a/gcc/gengtype-state.c b/gcc/gengtype-state.c
new file mode 100644 (file)
index 0000000..e6909f2
--- /dev/null
@@ -0,0 +1,2439 @@
+/* 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>
+*/
+
+#include "bconfig.h"
+#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, ids);
+      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, cstr);
+
+      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);
+  fprintf (state_file, ";;; file %s generated on %s\n", state_path,
+          ctime (&now));
+  /* 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);
+
+}
+\f
+/** 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;
+  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;
+  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;
+}
+
+
+/* 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, (htab_eq) strcmp, 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.  */
index 8d13d0eb1d177bf93b01efa6bb3cc87280439cd9..abf17f8e7f2a38ae8f2d1323ab7e8b32597d684c 100644 (file)
@@ -67,6 +67,10 @@ int do_debug;
 /* Level for verbose messages.  */
 int verbosity_level;
 
+/* We have a type count and use it to set the state_number of newly
+   allocated types to some unique negative number.  */
+static int type_count;
+
 /* The backup directory should be in the same file system as the
    generated files, otherwise the rename(2) system call would fail.
    If NULL, no backup is made when overwriting a generated file.  */
@@ -563,12 +567,14 @@ new_structure (const char *name, int isunion, struct fileloc *pos,
        else if (si->u.s.line.file != NULL && si->u.s.bitmap != bitmap)
          {
            ls = si;
+           type_count++;
            si = XCNEW (struct type);
            memcpy (si, ls, sizeof (struct type));
            ls->kind = TYPE_LANG_STRUCT;
            ls->u.s.lang_struct = si;
            ls->u.s.fields = NULL;
            si->next = NULL;
+           si->state_number = -type_count;
            si->pointer_to = NULL;
            si->u.s.lang_struct = ls;
          }
@@ -577,7 +583,9 @@ new_structure (const char *name, int isunion, struct fileloc *pos,
 
        if (ls != NULL && s == NULL)
          {
+           type_count++;
            s = XCNEW (struct type);
+           s->state_number = -type_count;
            s->next = ls->u.s.lang_struct;
            ls->u.s.lang_struct = s;
            s->u.s.lang_struct = ls;
@@ -587,7 +595,9 @@ new_structure (const char *name, int isunion, struct fileloc *pos,
 
   if (s == NULL)
     {
+      type_count++;
       s = XCNEW (struct type);
+      s->state_number = -type_count;
       s->next = structures;
       structures = s;
     }
@@ -625,8 +635,10 @@ find_structure (const char *name, int isunion)
     if (strcmp (name, s->u.s.tag) == 0 && UNION_P (s) == isunion)
       return s;
 
+  type_count++;
   s = XCNEW (struct type);
   s->next = structures;
+  s->state_number = -type_count;
   structures = s;
   s->kind = isunion ? TYPE_UNION : TYPE_STRUCT;
   s->u.s.tag = name;
@@ -650,9 +662,11 @@ find_param_structure (type_p t, type_p param[NUM_PARAM])
       break;
   if (res == NULL)
     {
+      type_count++;
       res = XCNEW (struct type);
       res->kind = TYPE_PARAM_STRUCT;
       res->next = param_structs;
+      res->state_number = -type_count;
       param_structs = res;
       res->u.param_struct.stru = t;
       memcpy (res->u.param_struct.param, param, sizeof (type_p) * NUM_PARAM);
@@ -679,6 +693,8 @@ create_pointer (type_p t)
   if (!t->pointer_to)
     {
       type_p r = XCNEW (struct type);
+      type_count++;
+      r->state_number = -type_count;
       r->kind = TYPE_POINTER;
       r->u.p = t;
       t->pointer_to = r;
@@ -693,8 +709,10 @@ create_array (type_p t, const char *len)
 {
   type_p v;
 
+  type_count++;
   v = XCNEW (struct type);
   v->kind = TYPE_ARRAY;
+  v->state_number = -type_count;
   v->u.a.p = t;
   v->u.a.len = len;
   return v;
@@ -4600,7 +4618,9 @@ dump_structures (const char *name, type_p structures)
   printf ("End of %s\n\n", name);
 }
 
-/* Dumps the internal structures of gengtype.  */
+/* Dumps the internal structures of gengtype.  This is useful to debug
+   gengtype itself, or to understand what it does, e.g. for plugin
+   developers.  */
 
 static void
 dump_everything (void)
@@ -4836,8 +4856,12 @@ main (int argc, char **argv)
   DBGPRINTF ("inputlist %s", inputlist);
   if (read_state_filename)
     {
-      fatal ("read state %s not implemented yet", read_state_filename);
-      /* TODO: implement read state.  */
+      if (inputlist)
+       fatal ("input list %s cannot be given with a read state file %s",
+              inputlist, read_state_filename);
+      read_state (read_state_filename);
+      DBGPRINT_COUNT_TYPE ("structures after read_state", structures);
+      DBGPRINT_COUNT_TYPE ("param_structs after read_state", param_structs);
     }
   else if (inputlist)
     {
@@ -4867,7 +4891,8 @@ main (int argc, char **argv)
                     (int) i, get_input_file_name (gt_files[i]));
        }
       if (verbosity_level >= 1)
-       printf ("%s parsed %d files\n", progname, (int) num_gt_files);
+       printf ("%s parsed %d files with %d GTY types\n", 
+               progname, (int) num_gt_files, type_count);
 
       DBGPRINT_COUNT_TYPE ("structures after parsing", structures);
       DBGPRINT_COUNT_TYPE ("param_structs after parsing", param_structs);
@@ -4892,7 +4917,7 @@ main (int argc, char **argv)
        fatal ("No plugin files given in plugin mode for %s",
               plugin_output_filename);
 
-      /* Parse our plugin files.  */
+      /* Parse our plugin files and augment the state.  */
       for (ix = 0; ix < nb_plugin_files; ix++)
        parse_file (get_input_file_name (plugin_files[ix]));
 
@@ -4917,11 +4942,30 @@ main (int argc, char **argv)
      hence enlarge the param_structs list of types.  */
   set_gc_used (variables);
 
-  /* We should write the state here, but it is not yet implemented.  */
+ /* The state at this point is read from the state input file or by
+    parsing source files and optionally augmented by parsing plugin
+    source files.  Write it now.  */
   if (write_state_filename)
     {
-      fatal ("write state %s in not yet implemented", write_state_filename);
-      /* TODO: implement write state.  */
+      DBGPRINT_COUNT_TYPE ("structures before write_state", structures);
+      DBGPRINT_COUNT_TYPE ("param_structs before write_state", param_structs);
+
+      if (hit_error)
+       fatal ("didn't write state file %s after errors", 
+              write_state_filename);
+
+      DBGPRINTF ("before write_state %s", write_state_filename);
+      write_state (write_state_filename);
+
+      if (do_dump)
+       dump_everything ();
+
+      /* After having written the state file we return immediately to
+        avoid generating any output file.  */
+      if (hit_error)
+       return 1;
+      else
+       return 0;
     }
 
 
index 2046e40d6e976d798022259903729ff6483f3bd9..136ab61fe75ed1a269df787fcd77ea7460829402 100644 (file)
@@ -374,6 +374,13 @@ extern size_t srcdir_len;
 extern const char *read_state_filename; /* (-r) program argument. */
 extern const char *write_state_filename; /* (-w) program argument. */
 
+/* Functions reading and writing the entire gengtype state, called from
+   main, and implemented in file gengtype-state.c.  */
+void read_state (const char* path);
+/* Write the state, and update the state_number field in types.  */
+void write_state (const char* path);
+
+
 /* Print an error message.  */
 extern void error_at_line
 (const struct fileloc *pos, const char *msg, ...) ATTRIBUTE_PRINTF_2;