1/* scan-decls.c - Extracts declarations from cpp output.
2   Copyright (C) 1993, 1995, 97-98, 1999 Free Software Foundation, Inc.
3
4This program is free software; you can redistribute it and/or modify it
5under the terms of the GNU General Public License as published by the
6Free Software Foundation; either version 2, or (at your option) any
7later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18   Written by Per Bothner <bothner@cygnus.com>, July 1993.  */
19
20#include "hconfig.h"
21#include "system.h"
22#include "cpplib.h"
23#include "scan.h"
24
25int brace_nesting = 0;
26
27/* The first extern_C_braces_length elements of extern_C_braces
28   indicate the (brace nesting levels of) left braces that were
29   prefixed by extern "C".  */
30int extern_C_braces_length = 0;
31char extern_C_braces[20];
32#define in_extern_C_brace (extern_C_braces_length>0)
33
34/* True if the function declaration currently being scanned is
35   prefixed by extern "C".  */
36int current_extern_C = 0;
37
38static void
39skip_to_closing_brace (pfile)
40     cpp_reader *pfile;
41{
42  int nesting = 1;
43  for (;;)
44    {
45      enum cpp_token token = cpp_get_token (pfile);
46      if (token == CPP_EOF)
47	break;
48      if (token == CPP_LBRACE)
49	nesting++;
50      if (token == CPP_RBRACE && --nesting == 0)
51	break;
52    }
53}
54
55/* This function scans a C source file (actually, the output of cpp),
56   reading from FP.  It looks for function declarations, and
57   external variable declarations.
58
59   The following grammar (as well as some extra stuff) is recognized:
60
61   declaration:
62     (decl-specifier)* declarator ("," declarator)* ";"
63   decl-specifier:
64     identifier
65     keyword
66     extern "C"
67   declarator:
68     (ptr-operator)* dname [ "(" argument-declaration-list ")" ]
69   ptr-operator:
70     ("*" | "&") ("const" | "volatile")*
71   dname:
72     identifier
73
74Here dname is the actual name being declared.
75*/
76
77int
78scan_decls (pfile, argc, argv)
79     cpp_reader *pfile;
80     int argc ATTRIBUTE_UNUSED;
81     char **argv ATTRIBUTE_UNUSED;
82{
83  int saw_extern, saw_inline;
84  int start_written;
85  /* If declarator_start is non-zero, it marks the start of the current
86     declarator.  If it is zero, we are either still parsing the
87     decl-specs, or prev_id_start marks the start of the declarator.  */
88  int declarator_start;
89  int prev_id_start, prev_id_end;
90  enum cpp_token token;
91
92 new_statement:
93  CPP_SET_WRITTEN (pfile, 0);
94  start_written = 0;
95  token = cpp_get_token (pfile);
96
97 handle_statement:
98  current_extern_C = 0;
99  saw_extern = 0;
100  saw_inline = 0;
101  if (token == CPP_RBRACE)
102    {
103      /* Pop an 'extern "C"' nesting level, if appropriate.  */
104      if (extern_C_braces_length
105	  && extern_C_braces[extern_C_braces_length - 1] == brace_nesting)
106	extern_C_braces_length--;
107      brace_nesting--;
108      goto new_statement;
109    }
110  if (token == CPP_LBRACE)
111    {
112      brace_nesting++;
113      goto new_statement;
114    }
115  if (token == CPP_EOF)
116    return 0;
117  if (token == CPP_SEMICOLON)
118    goto new_statement;
119  if (token != CPP_NAME)
120    goto new_statement;
121
122  prev_id_start = 0;
123  declarator_start = 0;
124  for (;;)
125    {
126      switch (token)
127	{
128	case CPP_LPAREN:
129	  /* Looks like this is the start of a formal parameter list.  */
130	  if (prev_id_start)
131	    {
132	      int nesting = 1;
133	      int have_arg_list = 0;
134	      cpp_buffer *fbuf = cpp_file_buffer (pfile);
135	      long func_lineno;
136	      cpp_buf_line_and_col (fbuf, &func_lineno, NULL);
137	      for (;;)
138		{
139		  token = cpp_get_token (pfile);
140		  if (token == CPP_LPAREN)
141		    nesting++;
142		  else if (token == CPP_RPAREN)
143		    {
144		      nesting--;
145		      if (nesting == 0)
146			break;
147		    }
148		  else if (token == CPP_EOF)
149		    break;
150		  else if (token == CPP_NAME || token == CPP_3DOTS)
151		    have_arg_list = 1;
152		}
153	      recognized_function (pfile->token_buffer + prev_id_start,
154				   prev_id_end - prev_id_start,
155				   (saw_inline ? 'I'
156				    : in_extern_C_brace || current_extern_C
157				    ? 'F' : 'f'),
158				   pfile->token_buffer, prev_id_start,
159				   have_arg_list,
160				   fbuf->nominal_fname, func_lineno);
161	      token = cpp_get_non_space_token (pfile);
162	      if (token == CPP_LBRACE)
163		{
164		  /* skip body of (normally) inline function */
165		  skip_to_closing_brace (pfile);
166		  goto new_statement;
167		}
168	      goto maybe_handle_comma;
169	    }
170	  break;
171	case CPP_OTHER:
172	  if (CPP_WRITTEN (pfile) == (size_t) start_written + 1
173	      && (CPP_PWRITTEN (pfile)[-1] == '*'
174		  || CPP_PWRITTEN (pfile)[-1] == '&'))
175	    declarator_start = start_written;
176	  else
177	    goto handle_statement;
178	  break;
179	case CPP_COMMA:
180	case CPP_SEMICOLON:
181	  if (prev_id_start && saw_extern)
182	    {
183	      recognized_extern (pfile->token_buffer + prev_id_start,
184				 prev_id_end - prev_id_start,
185				 pfile->token_buffer,
186				 prev_id_start);
187	    }
188	  /* ... fall through ...  */
189	maybe_handle_comma:
190	  if (token != CPP_COMMA)
191	    goto new_statement;
192#if 0
193	handle_comma:
194#endif
195	  /* Handle multiple declarators in a single declaration,
196	     as in:  extern char *strcpy (), *strcat (), ... ; */
197	  if (declarator_start == 0)
198	    declarator_start = prev_id_start;
199	  CPP_SET_WRITTEN (pfile, declarator_start);
200	  break;
201	case CPP_NAME:
202	  /* "inline" and "extern" are recognized but skipped */
203	  if (strcmp (pfile->token_buffer, "inline") == 0)
204	    {
205	      saw_inline = 1;
206	      CPP_SET_WRITTEN (pfile, start_written);
207	    }
208	  if (strcmp (pfile->token_buffer, "extern") == 0)
209	    {
210	      saw_extern = 1;
211	      CPP_SET_WRITTEN (pfile, start_written);
212	      token = cpp_get_non_space_token (pfile);
213	      if (token == CPP_STRING
214		  && strcmp (pfile->token_buffer, "\"C\"") == 0)
215		{
216		  CPP_SET_WRITTEN (pfile, start_written);
217		  current_extern_C = 1;
218		  token = cpp_get_non_space_token (pfile);
219		  if (token == CPP_LBRACE)
220		    {
221		      brace_nesting++;
222		      extern_C_braces[extern_C_braces_length++]
223			= brace_nesting;
224		      goto new_statement;
225		    }
226		}
227	      else
228		continue;
229	      break;
230	    }
231	  /* This may be the name of a variable or function.  */
232	  prev_id_start = start_written;
233	  prev_id_end = CPP_WRITTEN (pfile);
234	  break;
235
236	case CPP_EOF:
237	  return 0;
238
239	case CPP_LBRACE:  case CPP_RBRACE:  case CPP_DIRECTIVE:
240	  goto new_statement;  /* handle_statement? */
241
242	case CPP_HSPACE:  case CPP_VSPACE:  case CPP_COMMENT:  case CPP_POP:
243	  /* Skip initial white space.  */
244	  if (start_written == 0)
245	    CPP_SET_WRITTEN (pfile, 0);
246	  break;
247
248	 default:
249	  prev_id_start = 0;
250	}
251
252      start_written = CPP_WRITTEN (pfile);
253      token = cpp_get_token (pfile);
254    }
255}
256