OSDN Git Service

1999-12-17 13:21 -0800 Zack Weinberg <zack@rabi.columbia.edu>
[pf3gnuchains/gcc-fork.git] / gcc / fixinc / fixtests.c
1
2 /*
3
4    Test to see if a particular fix should be applied to a header file.
5
6    Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
7
8 = = = = = = = = = = = = = = = = = = = = = = = = =
9
10 NOTE TO DEVELOPERS
11
12 The routines you write here must work closely with both the fixincl.c
13 and the test_need.c program.
14
15 Here are the rules:
16
17 1.  Every test procedure name must be suffixed with "_test".
18     These routines will be referenced from inclhack.def, sans the suffix.
19
20 2.  Use the "TEST_FOR_FIX_PROC_HEAD()" macro _with_ the "_test" suffix
21     (I cannot use the ## magic from ANSI C) for defining your entry point.
22
23 3.  Put your test name into the FIX_TEST_TABLE
24
25 4.  Do not write anything to stdout.  It may be closed.
26
27 5.  Write to stderr only in the event of a reportable error
28     In such an event, call "exit(1)".
29
30 = = = = = = = = = = = = = = = = = = = = = = = = =
31
32 This file is part of GNU CC.
33
34 GNU CC is free software; you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation; either version 2, or (at your option)
37 any later version.
38
39 GNU CC is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42 GNU General Public License for more details.
43
44 You should have received a copy of the GNU General Public License
45 along with GNU CC; see the file COPYING.  If not, write to
46 the Free Software Foundation, 59 Temple Place - Suite 330,
47 Boston, MA 02111-1307, USA.  */
48
49 #include "fixlib.h"
50
51 typedef int apply_fix_p_t;  /* Apply Fix Predicate Type */
52
53 #define APPLY_FIX 0
54 #define SKIP_FIX  1
55
56 #define SHOULD_APPLY(afp) ((afp) == APPLY_FIX)
57 apply_fix_p_t run_test();
58
59 typedef struct {
60     const char*  test_name;
61     apply_fix_p_t (*test_proc)();
62 } test_entry_t;
63
64 #define FIX_TEST_TABLE \
65   _FT_( "double_slash",     double_slash_test ) \
66   _FT_( "else_endif_label", else_endif_label_test )
67
68
69 #define TEST_FOR_FIX_PROC_HEAD( test ) \
70 static apply_fix_p_t test ( fname, text ) \
71     const char* fname; \
72     const char* text;
73
74 /*
75  *  Skip over a quoted string.  Single quote strings may
76  *  contain multiple characters if the first character is
77  *  a backslash.  Especially a backslash followed by octal digits.
78  *  We are not doing a correctness syntax check here.
79  */
80 static const char*
81 skip_quote( q, text )
82   char  q;
83   char* text;
84 {
85   for (;;)
86     {
87       char ch = *(text++);
88       switch (ch)
89         {
90         case '\\':
91           text++; /* skip over whatever character follows */
92           break;
93
94         case '"':
95         case '\'':
96           if (ch != q)
97             break;
98           /*FALLTHROUGH*/
99
100         case '\n':
101         case NUL:
102           goto skip_done;
103         }
104     } skip_done:;
105
106   return text;
107 }
108
109 static apply_fix_p_t
110 is_cxx_header (fname, text)
111      const char *fname;
112      const char *text;
113 {
114   /*  First, check to see if the file is in a C++ directory */
115   if (strstr( fname, "CC/" ) != NULL)
116     return SKIP_FIX;
117   if (strstr( fname, "xx/" ) != NULL)
118     return SKIP_FIX;
119   if (strstr( fname, "++" ) != NULL)
120     return SKIP_FIX;
121   /* Or it might contain the phrase 'extern "C++"' */
122   if (strstr( text, "extern \"C++\"" ) != NULL)
123     return SKIP_FIX;
124
125   return APPLY_FIX;
126 }
127
128
129 TEST_FOR_FIX_PROC_HEAD( double_slash_test )
130 {
131   if (is_cxx_header (fname, text) == SKIP_FIX)
132     return SKIP_FIX;
133
134   /*  Now look for the comment markers in the text */
135   for (;;)
136     {
137       char ch = *(text++);
138       switch (ch)
139         {
140         case '/':
141           switch (*text) /* do not advance `text' here */
142             {
143             case '/':
144               /*
145                 We found a "//" pair in open text.
146                 The fix must be applied
147               */
148               return APPLY_FIX;
149
150             case '*':
151               /* We found a C-style comment.  Skip forward to the end */
152               text = strstr( text+1, "*/" );
153               if (text == (char*)NULL)
154                 goto test_done;
155               text += 2;
156             }
157           break;
158
159         case NUL:
160           goto test_done;
161
162         case '"':
163         case '\'':
164           text = skip_quote( ch, text );
165         }
166
167     } test_done:;
168
169   return SKIP_FIX;
170 }
171
172
173 TEST_FOR_FIX_PROC_HEAD( else_endif_label_test )
174 {
175   static int compiled = 0;
176   static const char label_pat[] = "^[ \t]*#[ \t]*(else|endif)";
177   static regex_t label_re;
178
179   char ch;
180   const char* pz_next = (char*)NULL;
181   regmatch_t match[2];
182   const char *all_text = text;
183
184   /*
185      This routine may be run many times within a single execution.
186      Do the compile once only in that case.  In the standalone case,
187      we waste 10 bytes of memory and a test, branch and increment delay.  */
188   if (! compiled)
189     {
190       compiled++;
191       re_set_syntax (RE_SYNTAX_EGREP);
192       (void)re_compile_pattern (label_pat, sizeof (label_pat)-1,
193                                 &label_re);
194     }
195
196   for (;;) /* entire file */
197     {
198       /*
199         See if we need to advance to the next candidate directive
200         If the scanning pointer passes over the end of the directive,
201         then the directive is inside a comment */
202       if (pz_next < text)
203         {
204           if (regexec (&label_re, text, 2, match, 0) != 0)
205             break;
206           pz_next = text + match[0].rm_eo;
207         }
208
209       /*
210         IF the scan pointer has not reached the directive end, ... */
211       if (pz_next > text)
212         {
213           /*
214             Advance the scanning pointer.  If we are at the start
215             of a quoted string or a comment, then skip the entire unit */
216           ch = *(text++);
217
218           switch (ch)
219             {
220             case '/':
221               /*
222                 Skip comments */
223               if (*text == '*')
224                 {
225                   text = strstr( text+1, "*/" );
226                   if (text == (char*)NULL)
227                     return SKIP_FIX;
228                   text += 2;
229                   continue;
230                 }
231               break;
232
233             case '"':
234             case '\'':
235               text = skip_quote( ch, text );
236               break;
237             } /* switch (ch) */
238           continue;
239         } /* if (still shy of directive end) */
240
241       /*
242          The scanning pointer (text) has reached the end of the current
243          directive under test, then check for bogons here */
244       for (;;) /* bogon check */
245         {
246           char ch = *(pz_next++);
247           if (isspace (ch))
248             {
249               if (ch == '\n')
250                 {
251                   /*
252                     It is clean.  No bogons on this directive */
253                   text = pz_next;
254                   pz_next = (char*)NULL; /* force a new regex search */
255                   break;
256                 }
257               continue;
258             }
259
260           switch (ch)
261             {
262             case '\\':
263               /*
264                 Skip escaped newlines.  Otherwise, we have a bogon */
265               if (*pz_next != '\n')
266                 return APPLY_FIX;
267
268               pz_next++;
269               break;
270
271             case '/':
272               /*
273                 Skip comments.  Otherwise, we have a bogon */
274               if (*pz_next == '*')
275                 {
276                   pz_next = strstr( pz_next+1, "*/" );
277                   if (pz_next == (char*)NULL)
278                     return SKIP_FIX;
279                   pz_next += 2;
280                   break;
281                 }
282               else if (*pz_next == '/'
283                        && is_cxx_header( fname, all_text ) == SKIP_FIX)
284                 {
285                   pz_next = strchr( pz_next+1, '\n' );
286                   if (pz_next == (char*)NULL)
287                     return SKIP_FIX;
288                   pz_next++;
289                   break;
290                 }
291
292               /* FALLTHROUGH */
293
294             default:
295               /*
296                 GOTTA BE A BOGON */
297               return APPLY_FIX;
298             } /* switch (ch) */
299         } /* for (bogon check loop) */
300     } /* for (entire file) loop */
301
302   return SKIP_FIX;
303 }
304
305 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
306
307      test for fix selector
308
309      THIS IS THE ONLY EXPORTED ROUTINE
310
311 */
312 apply_fix_p_t
313 run_test( tname, fname, text )
314   const char* tname;
315   const char* fname;
316   const char* text;
317 {
318 #define _FT_(n,p) { n, p },
319   static test_entry_t test_table[] = { FIX_TEST_TABLE { NULL, NULL }};
320 #undef _FT_
321 #define TEST_TABLE_CT ((sizeof(test_table)/sizeof(test_table[0]))-1)
322
323   int ct = TEST_TABLE_CT;
324   test_entry_t* pte = test_table;
325
326   do
327     {
328       if (strcmp( pte->test_name, tname ) == 0)
329         return (*pte->test_proc)( fname, text );
330       pte++;
331     } while (--ct > 0);
332   fprintf( stderr, "fixincludes error:  the `%s' fix test is unknown\n",
333            tname );
334   exit( 3 );
335 }
336
337 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
338
339      MAIN ROUTINE
340
341      This file is both included in fixincl.c and compiled as a separate
342      program for use by the inclhack.sh script.
343
344 */
345
346 #ifdef MAIN
347
348 int
349 main( argc, argv )
350   int argc;
351   char** argv;
352 {
353   char* fname = *++argv;
354   char* tname = *++argv;
355   char* buf;
356   size_t buf_size = 0;
357
358   if (argc != 3)
359     return run_test( "No test name provided", NULL, NULL, 0 );
360
361   fclose( stdin );
362   fclose( stdout );
363
364   buf = load_file_data (fopen (fname, "r"));
365
366   return run_test( tname, fname, buf );
367 }
368
369 #endif