| /* scan-decls.c - Extracts declarations from cpp output. | 
 |    Copyright (C) 1993, 1995, 1997, 1998, | 
 |    1999, 2000, 2003, 2004 Free Software Foundation, Inc. | 
 |  | 
 | This program is free software; you can redistribute it and/or modify it | 
 | under the terms of the GNU General Public License as published by the | 
 | Free Software Foundation; either version 2, or (at your option) any | 
 | later version. | 
 |  | 
 | This program 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 this program; if not, write to the Free Software | 
 | Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
 |  | 
 |    Written by Per Bothner <bothner@cygnus.com>, July 1993.  */ | 
 |  | 
 | #include "bconfig.h" | 
 | #include "system.h" | 
 | #include "coretypes.h" | 
 | #include "tm.h" | 
 | #include "cpplib.h" | 
 | #include "scan.h" | 
 |  | 
 | static void skip_to_closing_brace (cpp_reader *); | 
 | static const cpp_token *get_a_token (cpp_reader *); | 
 |  | 
 | int brace_nesting = 0; | 
 |  | 
 | /* The first extern_C_braces_length elements of extern_C_braces | 
 |    indicate the (brace nesting levels of) left braces that were | 
 |    prefixed by extern "C".  */ | 
 | int extern_C_braces_length = 0; | 
 | /* 20 is not enough anymore on Solaris 9.  */ | 
 | #define MAX_EXTERN_C_BRACES  200 | 
 | char extern_C_braces[MAX_EXTERN_C_BRACES]; | 
 | #define in_extern_C_brace (extern_C_braces_length>0) | 
 |  | 
 | /* APPLE LOCAL begin CW asm blocks */ | 
 | /* Dummies needed because we use them in cpplib, yuck.  */ | 
 | int flag_iasm_blocks; | 
 | int iasm_state; | 
 | bool iasm_in_operands; | 
 | /* APPLE LOCAL end CW asm blocks */ | 
 |  | 
 | /* True if the function declaration currently being scanned is | 
 |    prefixed by extern "C".  */ | 
 | int current_extern_C = 0; | 
 |  | 
 | /* Get a token but skip padding.  */ | 
 | static const cpp_token * | 
 | get_a_token (cpp_reader *pfile) | 
 | { | 
 |   for (;;) | 
 |     { | 
 |       const cpp_token *result = cpp_get_token (pfile); | 
 |       if (result->type != CPP_PADDING) | 
 | 	return result; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | skip_to_closing_brace (cpp_reader *pfile) | 
 | { | 
 |   int nesting = 1; | 
 |   for (;;) | 
 |     { | 
 |       enum cpp_ttype token = get_a_token (pfile)->type; | 
 |  | 
 |       if (token == CPP_EOF) | 
 | 	break; | 
 |       if (token == CPP_OPEN_BRACE) | 
 | 	nesting++; | 
 |       if (token == CPP_CLOSE_BRACE && --nesting == 0) | 
 | 	break; | 
 |     } | 
 | } | 
 |  | 
 | /* This function scans a C source file (actually, the output of cpp), | 
 |    reading from FP.  It looks for function declarations, and | 
 |    external variable declarations. | 
 |  | 
 |    The following grammar (as well as some extra stuff) is recognized: | 
 |  | 
 |    declaration: | 
 |      (decl-specifier)* declarator ("," declarator)* ";" | 
 |    decl-specifier: | 
 |      identifier | 
 |      keyword | 
 |      extern "C" | 
 |    declarator: | 
 |      (ptr-operator)* dname [ "(" argument-declaration-list ")" ] | 
 |    ptr-operator: | 
 |      ("*" | "&") ("const" | "volatile")* | 
 |    dname: | 
 |      identifier | 
 |  | 
 | Here dname is the actual name being declared. | 
 | */ | 
 |  | 
 | int | 
 | scan_decls (cpp_reader *pfile, int argc ATTRIBUTE_UNUSED, | 
 | 	    char **argv ATTRIBUTE_UNUSED) | 
 | { | 
 |   int saw_extern, saw_inline; | 
 |   cpp_token prev_id; | 
 |   const cpp_token *token; | 
 |  | 
 |  new_statement: | 
 |   token = get_a_token (pfile); | 
 |  | 
 |  handle_statement: | 
 |   current_extern_C = 0; | 
 |   saw_extern = 0; | 
 |   saw_inline = 0; | 
 |   if (token->type == CPP_OPEN_BRACE) | 
 |     { | 
 |       /* Pop an 'extern "C"' nesting level, if appropriate.  */ | 
 |       if (extern_C_braces_length | 
 | 	  && extern_C_braces[extern_C_braces_length - 1] == brace_nesting) | 
 | 	extern_C_braces_length--; | 
 |       brace_nesting--; | 
 |       goto new_statement; | 
 |     } | 
 |   if (token->type == CPP_OPEN_BRACE) | 
 |     { | 
 |       brace_nesting++; | 
 |       goto new_statement; | 
 |     } | 
 |  | 
 |   if (token->type == CPP_EOF) | 
 |     return 0; | 
 |  | 
 |   if (token->type == CPP_SEMICOLON) | 
 |     goto new_statement; | 
 |   if (token->type != CPP_NAME) | 
 |     goto new_statement; | 
 |  | 
 |   prev_id.type = CPP_EOF; | 
 |   for (;;) | 
 |     { | 
 |       switch (token->type) | 
 | 	{ | 
 | 	default: | 
 | 	  goto handle_statement; | 
 | 	case CPP_MULT: | 
 | 	case CPP_AND: | 
 | 	  /* skip */ | 
 | 	  break; | 
 |  | 
 | 	case CPP_COMMA: | 
 | 	case CPP_SEMICOLON: | 
 | 	  if (prev_id.type != CPP_EOF && saw_extern) | 
 | 	    { | 
 | 	      recognized_extern (&prev_id); | 
 | 	    } | 
 | 	  if (token->type == CPP_COMMA) | 
 | 	    break; | 
 | 	  /* ... fall through ...  */ | 
 | 	case CPP_OPEN_BRACE:  case CPP_CLOSE_BRACE: | 
 | 	  goto new_statement; | 
 |  | 
 | 	case CPP_EOF: | 
 | 	  return 0; | 
 |  | 
 | 	case CPP_OPEN_PAREN: | 
 | 	  /* Looks like this is the start of a formal parameter list.  */ | 
 | 	  if (prev_id.type != CPP_EOF) | 
 | 	    { | 
 | 	      int nesting = 1; | 
 | 	      int have_arg_list = 0; | 
 | 	      const struct line_map *map; | 
 | 	      unsigned int line; | 
 | 	      for (;;) | 
 | 		{ | 
 | 		  token = get_a_token (pfile); | 
 | 		  if (token->type == CPP_OPEN_PAREN) | 
 | 		    nesting++; | 
 | 		  else if (token->type == CPP_CLOSE_PAREN) | 
 | 		    { | 
 | 		      nesting--; | 
 | 		      if (nesting == 0) | 
 | 			break; | 
 | 		    } | 
 | 		  else if (token->type == CPP_EOF) | 
 | 		    break; | 
 | 		  else if (token->type == CPP_NAME | 
 | 			   || token->type == CPP_ELLIPSIS) | 
 | 		    have_arg_list = 1; | 
 | 		} | 
 | 	      map = linemap_lookup (&line_table, token->src_loc); | 
 | 	      line = SOURCE_LINE (map, token->src_loc); | 
 | 	      recognized_function (&prev_id, line, | 
 | 				   (saw_inline ? 'I' | 
 | 				    : in_extern_C_brace || current_extern_C | 
 | 				    ? 'F' : 'f'), have_arg_list); | 
 | 	      token = get_a_token (pfile); | 
 | 	      if (token->type == CPP_OPEN_BRACE) | 
 | 		{ | 
 | 		  /* skip body of (normally) inline function */ | 
 | 		  skip_to_closing_brace (pfile); | 
 | 		  goto new_statement; | 
 | 		} | 
 |  | 
 | 	      /* skip a possible __attribute__ or throw expression after the | 
 | 		 parameter list */ | 
 | 	      while (token->type != CPP_SEMICOLON && token->type != CPP_EOF) | 
 | 		token = get_a_token (pfile); | 
 | 	      if (token->type == CPP_EOF) | 
 | 		return 0; | 
 | 	      goto new_statement; | 
 | 	    } | 
 | 	  break; | 
 | 	case CPP_NAME: | 
 | 	  /* "inline" and "extern" are recognized but skipped */ | 
 | 	  if (cpp_ideq (token, "inline")) | 
 | 	    { | 
 | 	      saw_inline = 1; | 
 | 	    } | 
 | 	  else if (cpp_ideq (token, "extern")) | 
 | 	    { | 
 | 	      saw_extern = 1; | 
 | 	      token = get_a_token (pfile); | 
 | 	      if (token->type == CPP_STRING | 
 | 		  && token->val.str.len == 1 | 
 | 		  && token->val.str.text[0] == 'C') | 
 | 		{ | 
 | 		  current_extern_C = 1; | 
 | 		  token = get_a_token (pfile); | 
 | 		  if (token->type == CPP_OPEN_BRACE) | 
 | 		    { | 
 | 		      brace_nesting++; | 
 | 		      extern_C_braces[extern_C_braces_length++] | 
 | 			= brace_nesting; | 
 | 		      if (extern_C_braces_length >= MAX_EXTERN_C_BRACES) | 
 | 			{ | 
 | 			  fprintf (stderr, | 
 | 			  	   "Internal error: out-of-bounds index\n"); | 
 | 			  exit (FATAL_EXIT_CODE); | 
 | 			} | 
 | 		      goto new_statement; | 
 | 		    } | 
 | 		} | 
 | 	      else | 
 | 		continue; | 
 | 	      break; | 
 | 	    } | 
 | 	  /* This may be the name of a variable or function.  */ | 
 | 	  prev_id = *token; | 
 | 	  break; | 
 | 	} | 
 |       token = get_a_token (pfile); | 
 |     } | 
 | } |