mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			2619 lines
		
	
	
		
			69 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2619 lines
		
	
	
		
			69 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Gengtype persistent state serialization & de-serialization.
 | ||
|    Useful for gengtype in plugin mode.
 | ||
| 
 | ||
|    Copyright (C) 2010-2019 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 HOST_GENERATOR_FILE
 | ||
| #include "config.h"
 | ||
| #define GENERATOR_FILE 1
 | ||
| #else
 | ||
| #include "bconfig.h"
 | ||
| #endif
 | ||
| #include "system.h"
 | ||
| #include "errors.h"	/* For fatal.  */
 | ||
| #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:
 | ||
|     case TYPE_USER_STRUCT:
 | ||
|     case TYPE_UNDEFINED:
 | ||
|       return CONST_CAST (struct fileloc*, &ty->u.s.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, through 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 */
 | ||
| 
 | ||
| /* A class for writing out s-expressions, keeping track of newlines and
 | ||
|    nested indentation.  */
 | ||
| class s_expr_writer
 | ||
| {
 | ||
| public:
 | ||
|   s_expr_writer ();
 | ||
| 
 | ||
|   void write_new_line ();
 | ||
|   void write_any_indent (int leading_spaces);
 | ||
| 
 | ||
|   void begin_s_expr (const char *tag);
 | ||
|   void end_s_expr ();
 | ||
| 
 | ||
| private:
 | ||
|   int m_indent_amount;
 | ||
|   int m_had_recent_newline;
 | ||
| }; // class s_expr_writer
 | ||
| 
 | ||
| /* A class for writing out "gtype.state".  */
 | ||
| class state_writer : public s_expr_writer
 | ||
| {
 | ||
| public:
 | ||
|   state_writer ();
 | ||
| 
 | ||
| private:
 | ||
|   void write_state_fileloc (struct fileloc *floc);
 | ||
|   void write_state_fields (pair_p fields);
 | ||
|   void write_state_a_string (const char *s);
 | ||
|   void write_state_string_option (options_p current);
 | ||
|   void write_state_type_option (options_p current);
 | ||
|   void write_state_nested_option (options_p current);
 | ||
|   void write_state_option (options_p current);
 | ||
|   void write_state_options (options_p opt);
 | ||
|   void write_state_lang_bitmap (lang_bitmap bitmap);
 | ||
|   void write_state_version (const char *version);
 | ||
|   void write_state_scalar_type (type_p current);
 | ||
|   void write_state_string_type (type_p current);
 | ||
|   void write_state_undefined_type (type_p current);
 | ||
|   void write_state_struct_union_type (type_p current, const char *kindstr);
 | ||
|   void write_state_struct_type (type_p current);
 | ||
|   void write_state_user_struct_type (type_p current);
 | ||
|   void write_state_union_type (type_p current);
 | ||
|   void write_state_lang_struct_type (type_p current);
 | ||
|   void write_state_pointer_type (type_p current);
 | ||
|   void write_state_array_type (type_p current);
 | ||
|   void write_state_gc_used (enum gc_used_enum gus);
 | ||
|   void write_state_common_type_content (type_p current);
 | ||
|   void write_state_type (type_p current);
 | ||
|   void write_state_pair (pair_p current);
 | ||
|   int write_state_pair_list (pair_p list);
 | ||
|   void write_state_typedefs (void);
 | ||
|   void write_state_structures (void);
 | ||
|   void write_state_variables (void);
 | ||
|   void write_state_srcdir (void);
 | ||
|   void write_state_files_list (void);
 | ||
|   void write_state_languages (void);
 | ||
| 
 | ||
|   friend void write_state (const char *state_path);
 | ||
| 
 | ||
| private:
 | ||
|   /* Counter of written types.  */
 | ||
|   int m_state_written_type_count;
 | ||
| }; // class state_writer
 | ||
| 
 | ||
| 
 | ||
| /* class s_expr_writer's trivial constructor.  */
 | ||
| s_expr_writer::s_expr_writer ()
 | ||
|   : m_indent_amount (0),
 | ||
|     m_had_recent_newline (0)
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| /* Write a newline to the output file, merging adjacent newlines.  */
 | ||
| void
 | ||
| s_expr_writer::write_new_line (void)
 | ||
| {
 | ||
|   /* Don't add a newline if we've just had one.  */
 | ||
|   if (!m_had_recent_newline)
 | ||
|     {
 | ||
|       fprintf (state_file, "\n");
 | ||
|       m_had_recent_newline = 1;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* If we've just had a newline, write the indentation amount, potentially
 | ||
|    omitting some spaces.
 | ||
| 
 | ||
|    LEADING_SPACES exists to support code that writes strings with leading
 | ||
|    spaces (e.g " foo") which might occur within a line, or could be the first
 | ||
|    thing on a line.  By passing leading_spaces == 1, when such a string is the
 | ||
|    first thing on a line, write_any_indent () swallows the successive
 | ||
|    leading spaces into the indentation so that the "foo" begins at the expected
 | ||
|    column.  */
 | ||
| void
 | ||
| s_expr_writer::write_any_indent (int leading_spaces)
 | ||
| {
 | ||
|   int i;
 | ||
|   int amount = m_indent_amount - leading_spaces;
 | ||
|   if (m_had_recent_newline)
 | ||
|     for (i = 0; i < amount; i++)
 | ||
|       fprintf (state_file, " ");
 | ||
|   m_had_recent_newline = 0;
 | ||
| }
 | ||
| 
 | ||
| /* Write the beginning of a new s-expresion e.g. "(!foo "
 | ||
|    The writer automatically adds whitespace to show the hierarchical
 | ||
|    structure of the expressions, so each one starts on a new line,
 | ||
|    and any within it will be at an increased indentation level.  */
 | ||
| void
 | ||
| s_expr_writer::begin_s_expr (const char *tag)
 | ||
| {
 | ||
|   write_new_line ();
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "(!%s ", tag);
 | ||
|   m_indent_amount++;
 | ||
| }
 | ||
| 
 | ||
| /* Write out the end of an s-expression: any necssessary indentation,
 | ||
|    a closing parenthesis, and a new line.  */
 | ||
| void
 | ||
| s_expr_writer::end_s_expr (void)
 | ||
| {
 | ||
|   m_indent_amount--;
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, ")");
 | ||
|   write_new_line ();
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* class state_writer's trivial constructor.  */
 | ||
| state_writer::state_writer ()
 | ||
|   : s_expr_writer (),
 | ||
|     m_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 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
 | ||
|  *          - 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);
 | ||
| 
 | ||
| /* 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.  */
 | ||
| void
 | ||
| state_writer::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)
 | ||
| 	{
 | ||
| 	  begin_s_expr ("srcfileloc");
 | ||
| 	  write_state_a_string (srcrelpath);
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  begin_s_expr ("fileloc");
 | ||
| 	  write_state_a_string (get_input_file_name (floc->file));
 | ||
| 	}
 | ||
|       fprintf (state_file, " %d", floc->line);
 | ||
|       end_s_expr ();
 | ||
|     }
 | ||
|   else
 | ||
|     fprintf (state_file, "nil ");
 | ||
| }
 | ||
| 
 | ||
| /* Write a list of fields.  */
 | ||
| void
 | ||
| state_writer::write_state_fields (pair_p fields)
 | ||
| {
 | ||
|   int nbfields = pair_list_length (fields);
 | ||
|   int nbpairs = 0;
 | ||
|   begin_s_expr ("fields");
 | ||
|   fprintf (state_file, "%d ", nbfields);
 | ||
|   nbpairs = write_state_pair_list (fields);
 | ||
|   gcc_assert (nbpairs == nbfields);
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* Write a null-terminated string in our lexical convention, very
 | ||
|    similar to the convention of C.  */
 | ||
| void
 | ||
| state_writer::write_state_a_string (const char *s)
 | ||
| {
 | ||
|   char c;
 | ||
| 
 | ||
|   write_any_indent (1);
 | ||
| 
 | ||
|   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.  */
 | ||
| void
 | ||
| state_writer::write_state_string_option (options_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "string ");
 | ||
|   if (current->info.string != NULL)
 | ||
|     write_state_a_string (current->info.string);
 | ||
|   else
 | ||
|     fprintf (state_file, " nil ");
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| state_writer::write_state_type_option (options_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "type ");
 | ||
|   write_state_type (current->info.type);
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| state_writer::write_state_nested_option (options_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   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
 | ||
|     {
 | ||
|       write_any_indent (1);
 | ||
|       fprintf (state_file, " nil ");
 | ||
|     }
 | ||
| 
 | ||
|   if (current->info.nested->convert_to != NULL)
 | ||
|     write_state_a_string (current->info.nested->convert_to);
 | ||
|   else
 | ||
|     {
 | ||
|       write_any_indent (1);
 | ||
|       fprintf (state_file, " nil ");
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| state_writer::write_state_option (options_p current)
 | ||
| {
 | ||
|   begin_s_expr ("option");
 | ||
| 
 | ||
|   write_any_indent (0);
 | ||
|   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");
 | ||
|     }
 | ||
| 
 | ||
|   /* Terminate the "option" s-expression.  */
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /* Write a list of GTY options.  */
 | ||
| void
 | ||
| state_writer::write_state_options (options_p opt)
 | ||
| {
 | ||
|   options_p current;
 | ||
| 
 | ||
|   if (opt == NULL)
 | ||
|     {
 | ||
| 	write_any_indent (0);
 | ||
| 	fprintf (state_file, "nil ");
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   begin_s_expr ("options");
 | ||
|   for (current = opt; current != NULL; current = current->next)
 | ||
|       write_state_option (current);
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Write a bitmap representing a set of GCC front-end languages.  */
 | ||
| void
 | ||
| state_writer::write_state_lang_bitmap (lang_bitmap bitmap)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "%d ", (int) bitmap);
 | ||
| }
 | ||
| 
 | ||
| /* Write version information.  */
 | ||
| void
 | ||
| state_writer::write_state_version (const char *version)
 | ||
| {
 | ||
|   begin_s_expr ("version");
 | ||
|   write_state_a_string (version);
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* Write a scalar type.  We have only two of these.  */
 | ||
| void
 | ||
| state_writer::write_state_scalar_type (type_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   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! */
 | ||
| void
 | ||
| state_writer::write_state_string_type (type_p current)
 | ||
| {
 | ||
|   if (current == &string_type)
 | ||
|     {
 | ||
|       write_any_indent (0);
 | ||
|       fprintf (state_file, "string ");
 | ||
|       write_state_common_type_content (current);
 | ||
|     }
 | ||
|   else
 | ||
|     fatal ("Unexpected type in write_state_string_type");
 | ||
| }
 | ||
| 
 | ||
| /* Write an undefined type.  */
 | ||
| void
 | ||
| state_writer::write_state_undefined_type (type_p current)
 | ||
| {
 | ||
|   DBGPRINTF ("undefined type @ %p #%d '%s'", (void *) current,
 | ||
| 	     current->state_number, current->u.s.tag);
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "undefined ");
 | ||
|   gcc_assert (current->gc_used == GC_UNUSED);
 | ||
|   write_state_common_type_content (current);
 | ||
|   if (current->u.s.tag != NULL)
 | ||
|     write_state_a_string (current->u.s.tag);
 | ||
|   else
 | ||
|     {
 | ||
|       write_any_indent (0);
 | ||
|       fprintf (state_file, "nil");
 | ||
|     }
 | ||
| 
 | ||
|   write_state_fileloc (type_lineloc (current));
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Common code to write structure like types.  */
 | ||
| void
 | ||
| state_writer::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);
 | ||
|   write_any_indent (0);
 | ||
|   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
 | ||
|     {
 | ||
|       write_any_indent (0);
 | ||
|       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.  */
 | ||
| void
 | ||
| state_writer::write_state_struct_type (type_p current)
 | ||
| {
 | ||
|   write_state_struct_union_type (current, "struct");
 | ||
|   write_state_type (current->u.s.lang_struct);
 | ||
|   write_state_type (current->u.s.base_class);
 | ||
| }
 | ||
| 
 | ||
| /* Write a GTY user-defined struct type.  */
 | ||
| void
 | ||
| state_writer::write_state_user_struct_type (type_p current)
 | ||
| {
 | ||
|   DBGPRINTF ("user_struct type @ %p #%d '%s'", (void *) current,
 | ||
| 	     current->state_number, current->u.s.tag);
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "user_struct ");
 | ||
|   write_state_common_type_content (current);
 | ||
|   if (current->u.s.tag != NULL)
 | ||
|     write_state_a_string (current->u.s.tag);
 | ||
|   else
 | ||
|     {
 | ||
|       write_any_indent (0);
 | ||
|       fprintf (state_file, "nil");
 | ||
|     }
 | ||
|   write_state_fileloc (type_lineloc (current));
 | ||
|   write_state_fields (current->u.s.fields);
 | ||
| }
 | ||
| 
 | ||
| /* write a GTY union type.  */
 | ||
| void
 | ||
| state_writer::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.  */
 | ||
| void
 | ||
| state_writer::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 particularly 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);
 | ||
|     }
 | ||
|   begin_s_expr ("homotypes");
 | ||
|   fprintf (state_file, "%d", nbhomontype);
 | ||
|   for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next)
 | ||
|     write_state_type (hty);
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* Write a pointer type.  */
 | ||
| void
 | ||
| state_writer::write_state_pointer_type (type_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   fprintf (state_file, "pointer ");
 | ||
|   write_state_common_type_content (current);
 | ||
|   write_state_type (current->u.p);
 | ||
| }
 | ||
| 
 | ||
| /* Write an array type.  */
 | ||
| void
 | ||
| state_writer::write_state_array_type (type_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   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
 | ||
|     {
 | ||
|       write_any_indent (1);
 | ||
|       fprintf (state_file, " nil");
 | ||
|     }
 | ||
| 
 | ||
|   write_any_indent (1);
 | ||
|   fprintf (state_file, " ");
 | ||
|   write_state_type (current->u.a.p);
 | ||
| }
 | ||
| 
 | ||
| /* Write the gc_used information.  */
 | ||
| void
 | ||
| state_writer::write_state_gc_used (enum gc_used_enum gus)
 | ||
| {
 | ||
|   write_any_indent (1);
 | ||
|   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.  */
 | ||
| void
 | ||
| state_writer::write_state_common_type_content (type_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   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.  */
 | ||
| void
 | ||
| state_writer::write_state_type (type_p current)
 | ||
| {
 | ||
|   write_any_indent (0);
 | ||
|   if (current == NULL)
 | ||
|     {
 | ||
|       fprintf (state_file, "nil ");
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   begin_s_expr ("type");
 | ||
| 
 | ||
|   if (current->state_number > 0)
 | ||
|     {
 | ||
|       write_any_indent (0);
 | ||
|       fprintf (state_file, "already_seen %d", current->state_number);
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       m_state_written_type_count++;
 | ||
|       DBGPRINTF ("writing type #%d @%p old number %d", m_state_written_type_count,
 | ||
| 		 (void *) current, current->state_number);
 | ||
|       current->state_number = m_state_written_type_count;
 | ||
|       switch (current->kind)
 | ||
| 	{
 | ||
| 	case TYPE_NONE:
 | ||
| 	  gcc_unreachable ();
 | ||
| 	case TYPE_UNDEFINED:
 | ||
| 	  write_state_undefined_type (current);
 | ||
| 	  break;
 | ||
| 	case TYPE_STRUCT:
 | ||
| 	  write_state_struct_type (current);
 | ||
| 	  break;
 | ||
| 	case TYPE_USER_STRUCT:
 | ||
| 	  write_state_user_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_SCALAR:
 | ||
| 	  write_state_scalar_type (current);
 | ||
| 	  break;
 | ||
| 	case TYPE_STRING:
 | ||
| 	  write_state_string_type (current);
 | ||
| 	  break;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* Terminate the "type" s-expression.  */
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Write a pair.  */
 | ||
| void
 | ||
| state_writer::write_state_pair (pair_p current)
 | ||
| {
 | ||
|   if (current == NULL)
 | ||
|     {
 | ||
|       write_any_indent (0);
 | ||
|       fprintf (state_file, "nil)");
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   begin_s_expr ("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);
 | ||
| 
 | ||
|   /* Terminate the "pair" s-expression.  */
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* Write a pair list and return the number of pairs written.  */
 | ||
| int
 | ||
| state_writer::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, ... we count
 | ||
|    their length first and write it.  This eases the reading, and enables an
 | ||
|    extra verification on the number of actually read items.  */
 | ||
| 
 | ||
| /* Write our typedefs.  */
 | ||
| void
 | ||
| state_writer::write_state_typedefs (void)
 | ||
| {
 | ||
|   int nbtypedefs = pair_list_length (typedefs);
 | ||
|   int nbpairs = 0;
 | ||
|   begin_s_expr ("typedefs");
 | ||
|   fprintf (state_file, "%d", nbtypedefs);
 | ||
|   nbpairs = write_state_pair_list (typedefs);
 | ||
|   gcc_assert (nbpairs == nbtypedefs);
 | ||
|   end_s_expr ();
 | ||
|   if (verbosity_level >= 2)
 | ||
|     printf ("%s wrote %d typedefs\n", progname, nbtypedefs);
 | ||
| }
 | ||
| 
 | ||
| /* Write our structures.  */
 | ||
| void
 | ||
| state_writer::write_state_structures (void)
 | ||
| {
 | ||
|   int nbstruct = 0;
 | ||
|   type_p current;
 | ||
| 
 | ||
|   for (current = structures; current != NULL; current = current->next)
 | ||
|     nbstruct++;
 | ||
| 
 | ||
|   begin_s_expr ("structures");
 | ||
|   fprintf (state_file, "%d", nbstruct);
 | ||
| 
 | ||
|   for (current = structures; current != NULL; current = current->next)
 | ||
|     {
 | ||
|       write_new_line ();
 | ||
|       write_state_type (current);
 | ||
|     }
 | ||
| 
 | ||
|   /* Terminate the "structures" s-expression.  */
 | ||
|   end_s_expr ();
 | ||
|   if (verbosity_level >= 2)
 | ||
|     printf ("%s wrote %d structures in state\n", progname, nbstruct);
 | ||
| }
 | ||
| 
 | ||
| /* Write our variables.  */
 | ||
| void
 | ||
| state_writer::write_state_variables (void)
 | ||
| {
 | ||
|   int nbvars = pair_list_length (variables);
 | ||
|   int nbpairs = 0;
 | ||
|   begin_s_expr ("variables");
 | ||
|   fprintf (state_file, "%d", nbvars);
 | ||
|   nbpairs = write_state_pair_list (variables);
 | ||
|   gcc_assert (nbpairs == nbvars);
 | ||
|   end_s_expr ();
 | ||
|   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.  */
 | ||
| void
 | ||
| state_writer::write_state_srcdir (void)
 | ||
| {
 | ||
|   begin_s_expr ("srcdir");
 | ||
|   write_state_a_string (srcdir);
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* Count and write the list of our files.  */
 | ||
| void
 | ||
| state_writer::write_state_files_list (void)
 | ||
| {
 | ||
|   int i = 0;
 | ||
|   /* Write the list of files with their lang_bitmap.  */
 | ||
|   begin_s_expr ("fileslist");
 | ||
|   fprintf (state_file, "%d", (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)
 | ||
| 	{
 | ||
| 	  begin_s_expr ("srcfile");
 | ||
| 	  fprintf (state_file, "%d ", get_lang_bitmap (curfil));
 | ||
| 	  write_state_a_string (cursrcrelpath);
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  begin_s_expr ("file");
 | ||
| 	  fprintf (state_file, "%d ", get_lang_bitmap (curfil));
 | ||
| 	  write_state_a_string (get_input_file_name (curfil));
 | ||
| 	}
 | ||
|       /* Terminate the inner s-expression (either "srcfile" or "file").   */
 | ||
|       end_s_expr ();
 | ||
|     }
 | ||
|   /* Terminate the "fileslist" s-expression.  */
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* Write the list of GCC front-end languages.  */
 | ||
| void
 | ||
| state_writer::write_state_languages (void)
 | ||
| {
 | ||
|   int i = 0;
 | ||
|   begin_s_expr ("languages");
 | ||
|   fprintf (state_file, "%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]);
 | ||
|     }
 | ||
|   end_s_expr ();
 | ||
| }
 | ||
| 
 | ||
| /* 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);
 | ||
| 
 | ||
|   state_writer sw;
 | ||
| 
 | ||
|   /* The first non-comment significant line gives the version string.  */
 | ||
|   sw.write_state_version (version_string);
 | ||
|   sw.write_state_srcdir ();
 | ||
|   sw.write_state_languages ();
 | ||
|   sw.write_state_files_list ();
 | ||
|   sw.write_state_structures ();
 | ||
|   sw.write_state_typedefs ();
 | ||
|   sw.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, sw.m_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 an undefined type.  */
 | ||
| static void
 | ||
| read_state_undefined_type (type_p type)
 | ||
| {
 | ||
|   struct state_token_st *t0;
 | ||
| 
 | ||
|   type->kind = TYPE_UNDEFINED;
 | ||
|   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 undefined type @%p #%d",
 | ||
| 		     (void *) type, type->state_number);
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
 | ||
| 	  DBGPRINTF ("read undefined type @%p #%d '%s'",
 | ||
| 		     (void *) type, type->state_number, type->u.s.tag);
 | ||
| 	}
 | ||
| 
 | ||
|       next_state_tokens (1);
 | ||
|       read_state_fileloc (&(type->u.s.line));
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       fatal_reading_state (t0, "Bad tag in undefined type");
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* 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));
 | ||
|       read_state_type (&(type->u.s.base_class));
 | ||
|       if (type->u.s.base_class)
 | ||
| 	add_subclass (type->u.s.base_class, type);
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       fatal_reading_state (t0, "Bad tag in struct type");
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Read a GTY-ed user-provided struct TYPE.  */
 | ||
| 
 | ||
| static void
 | ||
| read_state_user_struct_type (type_p type)
 | ||
| {
 | ||
|   struct state_token_st *t0;
 | ||
| 
 | ||
|   type->kind = TYPE_USER_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));
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       fatal_reading_state (t0, "Bad tag in user-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 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 (¤t->pointer_to);
 | ||
|   read_state_gc_used (¤t->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, "undefined"))
 | ||
| 	    {
 | ||
| 	      *current = XCNEW (struct type);
 | ||
| 	      next_state_tokens (1);
 | ||
| 	      read_state_undefined_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, "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 if (state_token_is_name (t0, "user_struct"))
 | ||
| 	    {
 | ||
| 	      *current = XCNEW (struct type);
 | ||
| 	      next_state_tokens (1);
 | ||
| 	      read_state_user_struct_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 (¤t_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 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_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.  */
 |