OSDN Git Service

2000-02-04 Bruce Korb <bkorb@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / fixinc / fixfixes.c
1
2 /*
3
4    Test to see if a particular fix should be applied to a header file.
5
6    Copyright (C) 1997-1999, 2000 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 "_fix".
18     These routines will be referenced from inclhack.def, sans the suffix.
19
20 2.  Use the "FIX_PROC_HEAD()" macro _with_ the "_fix" suffix
21     (I cannot use the ## magic from ANSI C) for defining your entry point.
22
23 3.  Put your test name into the FIXUP_TABLE
24
25 4.  Do not read anything from stdin.  It is 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 6.  If "MAIN" is _not_ defined, then you have access to the fixDescList
31     entry for the fix in question.  This may be useful, for example,
32     if there are pre-compiled selection expressions stored there.
33
34     For example, you may do this if you know that the first 
35     test contains a useful regex.  This is okay because, remember,
36     this code perforce works closely with the inclhack.def fixes!!
37
38
39     tFixDesc*  pMyDesc = fixDescList + MY_FIX_NAME_FIXIDX;
40     tTestDesc* pTestList = pMyDesc->p_test_desc;
41
42     regexec (pTestList->p_test_regex, ...)
43
44
45     If MAIN _is_ defined, then you will have to compile it on
46     your own.
47
48 = = = = = = = = = = = = = = = = = = = = = = = = =
49
50 This file is part of GNU CC.
51
52 GNU CC is free software; you can redistribute it and/or modify
53 it under the terms of the GNU General Public License as published by
54 the Free Software Foundation; either version 2, or (at your option)
55 any later version.
56
57 GNU CC is distributed in the hope that it will be useful,
58 but WITHOUT ANY WARRANTY; without even the implied warranty of
59 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
60 GNU General Public License for more details.
61
62 You should have received a copy of the GNU General Public License
63 along with GNU CC; see the file COPYING.  If not, write to
64 the Free Software Foundation, 59 Temple Place - Suite 330,
65 Boston, MA 02111-1307, USA.  */
66
67 #include "fixlib.h"
68
69 typedef struct {
70     const char*  fix_name;
71     void (*fix_proc)();
72 } fix_entry_t;
73
74 #define FIXUP_TABLE \
75   _FT_( "no_double_slash",  double_slash_fix ) \
76   _FT_( "else_endif_label", else_endif_label_fix ) \
77   _FT_( "IO_use",           IO_use_fix ) \
78   _FT_( "CTRL_use",         CTRL_use_fix) \
79   _FT_( "IO_defn",          IO_defn_fix ) \
80   _FT_( "CTRL_defn",        CTRL_defn_fix ) \
81   _FT_( "machine_name",     machine_name_fix )
82
83
84 #define FIX_PROC_HEAD( fix ) \
85 static void fix ( filname, text ) \
86     const char* filname; \
87     char* text;
88
89
90 /*
91  *  Skip over a quoted string.  Single quote strings may
92  *  contain multiple characters if the first character is
93  *  a backslash.  Especially a backslash followed by octal digits.
94  *  We are not doing a correctness syntax check here.
95  */
96 static char*
97 print_quote( q, text )
98   char  q;
99   char* text;
100 {
101   fputc( q, stdout );
102
103   for (;;)
104     {
105       char ch = *(text++);
106       fputc( ch, stdout );
107
108       switch (ch)
109         {
110         case '\\':
111           if (*text == NUL)
112             goto quote_done;
113
114           fputc( *(text++), stdout );
115           break;
116
117         case '"':
118         case '\'':
119           if (ch != q)
120             break;
121           /*FALLTHROUGH*/
122
123         case '\n':
124         case NUL:
125           goto quote_done;
126         }
127     } quote_done:;
128
129   return text;
130 }
131
132
133 FIX_PROC_HEAD( double_slash_fix )
134 {
135   /*  Now look for the comment markers in the text */
136   for (;;)
137     {
138       char ch = *(text++);
139       switch (ch)
140         {
141         case '/':
142           switch (*text) /* do not advance `text' here */
143             {
144             case '/':
145               /*
146                 We found a "//" pair in open text.
147                 Delete text to New-Line
148               */
149               while ((*text != '\n') && (*text != '\0'))  text++;
150               break;
151
152             case '*':
153               {
154                 /* We found a C-style comment.  Skip forward to the end */
155                 char* pz = strstr( (--text)+2, "*/" );
156                 if (pz == (char*)NULL)
157                   {
158                     fputs( text, stdout );
159                     goto fix_done;
160                   }
161                 pz += 2;
162                 fwrite (text, (pz - text), 1, stdout );
163                 text = pz;
164               }
165               break;
166
167             default:
168               fputc (ch, stdout );
169             }
170           break;
171
172         case NUL:
173           goto fix_done;
174
175         case '"':
176         case '\'':
177           text = print_quote (ch, text );
178           break;
179
180         default:
181           fputc (ch, stdout );
182         }
183
184     } fix_done:;
185
186   fclose (stdout);;
187 }
188
189
190 FIX_PROC_HEAD( else_endif_label_fix )
191 {
192   static const char label_pat[] = "^[ \t]*#[ \t]*(else|endif)";
193   static regex_t label_re;
194
195   char ch;
196   char* pz_next = (char*)NULL;
197   regmatch_t match[2];
198
199   compile_re (label_pat, &label_re, 1,
200               "label pattern", "else_endif_label_fix");
201
202   for (;;) /* entire file */
203     {
204       /*
205         See if we need to advance to the next candidate directive
206         If the scanning pointer passes over the end of the directive,
207         then the directive is inside a comment */
208       if (pz_next < text)
209         {
210           if (regexec (&label_re, text, 2, match, 0) != 0)
211             {
212               fputs( text, stdout );
213               break;
214             }
215
216           pz_next = text + match[0].rm_eo;
217         }
218
219       /*
220         IF the scan pointer has not reached the directive end, ... */
221       if (pz_next > text)
222         {
223           /*
224             Advance the scanning pointer.  If we are at the start
225             of a quoted string or a comment, then skip the entire unit */
226           ch = *text;
227
228           switch (ch)
229             {
230             case '/':
231               /*
232                 Skip comments */
233               if (text[1] == '*')
234                 {
235                   char* pz = strstr( text+2, "*/" );
236                   if (pz == (char*)NULL)
237                     {
238                       fputs( text, stdout );
239                       return;
240                     }
241                   pz += 2;
242                   fwrite( text, 1, (pz - text), stdout );
243                   text = pz;
244                   continue;
245                 }
246               putc( ch, stdout );
247               text++;
248               break;
249
250             case '"':
251             case '\'':
252               text = print_quote( ch, text+1 );
253               break;
254
255             default:
256               putc( ch, stdout );
257               text++;
258             } /* switch (ch) */
259           continue;
260         } /* if (still shy of directive end) */
261
262       /*
263          The scanning pointer (text) has reached the end of the current
264          directive under test.  Check for bogons here.  */
265       for (;;) /* bogon check */
266         {
267           char ch = *(text++);
268           if (isspace (ch))
269             {
270               putc( ch, stdout );
271               if (ch == '\n')
272                 {
273                   /*
274                     It is clean.  No bogons on this directive */
275                   pz_next = (char*)NULL; /* force a new regex search */
276                   goto dont_fix_bogon;
277                 }
278               continue;
279             }
280
281           switch (ch)
282             {
283             case NUL:
284               return;
285
286             case '\\':
287               /*
288                 Skip escaped newlines.  Otherwise, we have a bogon */
289               if (*text != '\n') {
290                 text--;
291                 goto fix_the_bogon;
292               }
293
294               /*
295                 Emit the escaped newline and keep scanning for possible junk */
296               putc( '\\', stdout );
297               putc( '\n', stdout );
298               text++;
299               break;
300
301             case '/':
302               /*
303                 Skip comments.  Otherwise, we have a bogon */
304               if (*text == '*')
305                 {
306                   text--;
307                   pz_next = strstr( text+2, "*/" );
308                   if (pz_next == (char*)NULL)
309                     {
310                       putc( '\n', stdout );
311                       return;
312                     }
313                   pz_next += 2;
314                   fwrite( text, 1, (pz_next - text), stdout );
315                   text = pz_next;
316                   break;
317                 }
318
319               /* FALLTHROUGH */
320
321             default:
322               /*
323                 GOTTA BE A BOGON */
324               text--;
325               goto fix_the_bogon;
326             } /* switch (ch) */
327         } /* for (bogon check loop) */
328
329     fix_the_bogon:
330       /*
331         `text' points to the start of the bogus data */
332       for (;;)
333         {
334           /*
335             NOT an escaped newline.  Find the end of line that
336             is not preceeded by an escape character:  */
337           pz_next = strchr( text, '\n' );
338           if (pz_next == (char*)NULL)
339             {
340               putc( '\n', stdout );
341               return;
342             }
343
344           if (pz_next[-1] != '\\')
345             {
346               text = pz_next;
347               pz_next = (char*)NULL; /* force a new regex search */
348               break;
349             }
350
351           /*
352             The newline was escaped.  We gotta keep going.  */
353           text = pz_next + 1;
354         }
355
356     dont_fix_bogon:;
357     } /* for (entire file) loop */
358
359   return;
360 }
361
362 /* Scan the input file for all occurrences of text like this:
363
364    #define TIOCCONS _IO(T, 12)
365
366    and change them to read like this:
367
368    #define TIOCCONS _IO('T', 12)
369
370    which is the required syntax per the C standard.  (The definition of
371    _IO also has to be tweaked - see below.)  'IO' is actually whatever you
372    provide in the STR argument.  */
373 void
374 fix_char_macro_uses (text, str)
375      const char *text;
376      const char *str;
377 {
378   /* This regexp looks for a traditional-syntax #define (# in column 1)
379      of an object-like macro.  */
380   static const char pat[] =
381     "^#[ \t]*define[ \t]+[_A-Za-z][_A-Za-z0-9]*[ \t]+";
382   static regex_t re;
383
384   regmatch_t rm[1];
385   const char *p, *limit;
386   size_t len = strlen (str);
387
388   compile_re (pat, &re, 1, "macro pattern", "fix_char_macro_uses");
389
390   for (p = text;
391        regexec (&re, p, 1, rm, 0) == 0;
392        p = limit + 1)
393     {
394       /* p + rm[0].rm_eo is the first character of the macro replacement.
395          Find the end of the macro replacement, and the STR we were
396          sent to look for within the replacement.  */
397       p += rm[0].rm_eo;
398       limit = p - 1;
399       do
400         {
401           limit = strchr (limit + 1, '\n');
402           if (!limit)
403             goto done;
404         }
405       while (limit[-1] == '\\');
406
407       do
408         {
409           if (*p == str[0] && !strncmp (p+1, str+1, len-1))
410             goto found;
411         }
412       while (++p < limit - len);
413       /* Hit end of line.  */
414       continue;
415
416     found:
417       /* Found STR on this line.  If the macro needs fixing,
418          the next few chars will be whitespace or uppercase,
419          then an open paren, then a single letter.  */
420       while ((isspace (*p) || isupper (*p)) && p < limit) p++;
421       if (*p++ != '(')
422         continue;
423       if (!isalpha (*p))
424         continue;
425       if (isalnum (p[1]) || p[1] == '_')
426         continue;
427
428       /* Splat all preceding text into the output buffer,
429          quote the character at p, then proceed.  */
430       fwrite (text, 1, p - text, stdout);
431       putchar ('\'');
432       putchar (*p);
433       putchar ('\'');
434       text = p + 1;
435     }
436  done:
437   fputs (text, stdout);
438 }
439
440 /* Scan the input file for all occurrences of text like this:
441
442    #define _IO(x, y) ('x'<<16+y)
443
444    and change them to read like this:
445
446    #define _IO(x, y) (x<<16+y)
447
448    which is the required syntax per the C standard.  (The uses of _IO
449    also have to be tweaked - see above.)  'IO' is actually whatever
450    you provide in the STR argument.  */
451 void
452 fix_char_macro_defines (text, str)
453      const char *text;
454      const char *str;
455 {
456   /* This regexp looks for any traditional-syntax #define (# in column 1).  */
457   static const char pat[] =
458     "^#[ \t]*define[ \t]+";
459   static regex_t re;
460
461   regmatch_t rm[1];
462   const char *p, *limit;
463   size_t len = strlen (str);
464   char arg;
465
466   compile_re (pat, &re, 1, "macro pattern", "fix_char_macro_defines");
467
468   for (p = text;
469        regexec (&re, p, 1, rm, 0) == 0;
470        p = limit + 1)
471     {
472       /* p + rm[0].rm_eo is the first character of the macro name.
473          Find the end of the macro replacement, and the STR we were
474          sent to look for within the name.  */
475       p += rm[0].rm_eo;
476       limit = p - 1;
477       do
478         {
479           limit = strchr (limit + 1, '\n');
480           if (!limit)
481             goto done;
482         }
483       while (limit[-1] == '\\');
484
485       do
486         {
487           if (*p == str[0] && !strncmp (p+1, str+1, len-1))
488             goto found;
489           p++;
490         }
491       while (isalpha (*p) || isalnum (*p) || *p == '_');
492       /* Hit end of macro name without finding the string.  */
493       continue;
494
495     found:
496       /* Found STR in this macro name.  If the macro needs fixing,
497          there may be a few uppercase letters, then there will be an
498          open paren with _no_ intervening whitespace, and then a
499          single letter.  */
500       while (isupper (*p) && p < limit) p++;
501       if (*p++ != '(')
502         continue;
503       if (!isalpha (*p))
504         continue;
505       if (isalnum (p[1]) || p[1] == '_')
506         continue;
507
508       /* The character at P is the one to look for in the following
509          text.  */
510       arg = *p;
511       p += 2;
512
513       while (p < limit)
514         {
515           if (p[-1] == '\'' && p[0] == arg && p[1] == '\'')
516             {
517               /* Remove the quotes from this use of ARG.  */
518               p--;
519               fwrite (text, 1, p - text, stdout);
520               putchar (arg);
521               p += 3;
522               text = p;
523             }
524           else
525             p++;
526         }
527     }
528  done:
529   fputs (text, stdout);
530 }
531
532 /* The various prefixes on these macros are handled automatically
533    because the fixers don't care where they start matching.  */
534 FIX_PROC_HEAD( IO_use_fix )
535 {
536   fix_char_macro_uses (text, "IO");
537 }
538 FIX_PROC_HEAD( CTRL_use_fix )
539 {
540   fix_char_macro_uses (text, "CTRL");
541 }
542
543 FIX_PROC_HEAD( IO_defn_fix )
544 {
545   fix_char_macro_defines (text, "IO");
546 }
547 FIX_PROC_HEAD( CTRL_defn_fix )
548 {
549   fix_char_macro_defines (text, "CTRL");
550 }
551
552
553 /* Fix for machine name #ifdefs that are not in the namespace reserved
554    by the C standard.  They won't be defined if compiling with -ansi,
555    and the headers will break.  We go to some trouble to only change
556    #ifdefs where the macro is defined by GCC in non-ansi mode; this
557    minimizes the number of headers touched.  */
558
559 #define SCRATCHSZ 64   /* hopefully long enough */
560
561 FIX_PROC_HEAD( machine_name_fix )
562 {
563 #ifndef MN_NAME_PAT
564   fputs( "The target machine has no needed machine name fixes\n", stderr );
565 #else
566   regmatch_t match[2];
567   char *line, *base, *limit, *p, *q;
568   regex_t *label_re, *name_re;
569   char scratch[SCRATCHSZ];
570   size_t len;
571
572   mn_get_regexps (&label_re, &name_re, "machine_name_fix");
573
574   scratch[0] = '_';
575   scratch[1] = '_';
576
577   for (base = text;
578        regexec (label_re, base, 2, match, 0) == 0;
579        base = limit)
580     {
581       base += match[0].rm_eo;
582       /* We're looking at an #if or #ifdef.  Scan forward for the
583          next non-escaped newline.  */
584       line = limit = base;
585       do
586         {
587           limit++;
588           limit = strchr (limit, '\n');
589           if (!limit)
590             goto done;
591         }
592       while (limit[-1] == '\\');
593
594       /* If the 'name_pat' matches in between base and limit, we have
595          a bogon.  It is not worth the hassle of excluding comments
596          because comments on #if/#ifdef lines are rare, and strings on
597          such lines are illegal.
598
599          REG_NOTBOL means 'base' is not at the beginning of a line, which
600          shouldn't matter since the name_re has no ^ anchor, but let's
601          be accurate anyway.  */
602
603       for (;;)
604         {
605         again:
606           if (base == limit)
607             break;
608
609           if (regexec (name_re, base, 1, match, REG_NOTBOL))
610             goto done;  /* No remaining match in this file */
611
612           /* Match; is it on the line?  */
613           if (match[0].rm_eo > limit - base)
614             break;
615
616           p = base + match[0].rm_so;
617           base += match[0].rm_eo;
618
619           /* One more test: if on the same line we have the same string
620              with the appropriate underscores, then leave it alone.
621              We want exactly two leading and trailing underscores.  */
622           if (*p == '_')
623             {
624               len = base - p - ((*base == '_') ? 2 : 1);
625               q = p + 1;
626             }
627           else
628             {
629               len = base - p - ((*base == '_') ? 1 : 0);
630               q = p;
631             }
632           if (len + 4 > SCRATCHSZ)
633             abort ();
634           memcpy (&scratch[2], q, len);
635           len += 2;
636           scratch[len++] = '_';
637           scratch[len++] = '_';
638
639           for (q = line; q <= limit - len; q++)
640             if (*q == '_' && !strncmp (q, scratch, len))
641               goto again;
642           
643           fwrite (text, 1, p - text, stdout);
644           fwrite (scratch, 1, len, stdout);
645
646           text = base;
647         }
648     }
649  done:
650 #endif
651   fputs (text, stdout);
652 }
653
654
655 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
656
657      test for fix selector
658
659      THIS IS THE ONLY EXPORTED ROUTINE
660
661 */
662 void
663 apply_fix( fixname, filname )
664   const char* fixname;
665   const char* filname;
666 {
667 #define _FT_(n,p) { n, p },
668   static fix_entry_t fix_table[] = { FIXUP_TABLE { NULL, NULL }};
669 #undef _FT_
670 #define FIX_TABLE_CT ((sizeof(fix_table)/sizeof(fix_table[0]))-1)
671
672   char* buf;
673   int ct = FIX_TABLE_CT;
674   fix_entry_t* pfe = fix_table;
675
676   for (;;)
677     {
678       if (strcmp (pfe->fix_name, fixname) == 0)
679         break;
680       if (--ct <= 0)
681         {
682           fprintf (stderr, "fixincludes error:  the `%s' fix is unknown\n",
683                    fixname );
684           exit (3);
685         }
686       pfe++;
687     }
688
689   buf = load_file_data (stdin);
690   (*pfe->fix_proc)( filname, buf );
691 }
692
693 #ifdef MAIN
694
695 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
696
697      MAIN ROUTINE
698
699      This file is both included in fixincl.c and compiled as a separate
700      program for use by the inclhack.sh script.
701
702 */
703
704 int
705 main( argc, argv )
706   int argc;
707   char** argv;
708 {
709   if (argc != 3)
710     apply_fix ("No test name provided", NULL, NULL, 0 );
711
712   apply_fix (argv[2], argv[1]);
713   return 0;
714 }
715
716 #endif