fixfixes.c 15.4 KB
Newer Older
korbb's avatar
korbb committed
1 2 3 4 5

/*

   Test to see if a particular fix should be applied to a header file.

law's avatar
law committed
6
   Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
korbb's avatar
korbb committed
7 8 9 10 11

= = = = = = = = = = = = = = = = = = = = = = = = =

NOTE TO DEVELOPERS

12
The routines you write here must work closely with fixincl.c.
korbb's avatar
korbb committed
13 14 15 16 17 18 19 20 21

Here are the rules:

1.  Every test procedure name must be suffixed with "_fix".
    These routines will be referenced from inclhack.def, sans the suffix.

2.  Use the "FIX_PROC_HEAD()" macro _with_ the "_fix" suffix
    (I cannot use the ## magic from ANSI C) for defining your entry point.

korbb's avatar
korbb committed
22
3.  Put your test name into the FIXUP_TABLE.
korbb's avatar
korbb committed
23 24 25 26

4.  Do not read anything from stdin.  It is closed.

5.  Write to stderr only in the event of a reportable error
27
    In such an event, call "exit (EXIT_FAILURE)".
korbb's avatar
korbb committed
28

29
6.  You have access to the fixDescList entry for the fix in question.
korbb's avatar
korbb committed
30 31
    This may be useful, for example, if there are interesting strings
    or pre-compiled regular expressions stored there.
korbb's avatar
korbb committed
32

korbb's avatar
korbb committed
33 34
    It is also possible to access fix descriptions by using the
    index of a known fix, "my_fix_name" for example:
korbb's avatar
korbb committed
35

korbb's avatar
korbb committed
36 37
        tFixDesc*  p_desc  = fixDescList + MY_FIX_NAME_FIXIDX;
        tTestDesc* p_tlist = p_desc->p_test_desc;
korbb's avatar
korbb committed
38

korbb's avatar
korbb committed
39
        regexec (p_tlist->p_test_regex, ...)
korbb's avatar
korbb committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

= = = = = = = = = = = = = = = = = = = = = = = = =

This file is part of GNU CC.

GNU CC 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.

GNU CC 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "fixlib.h"

62 63 64 65
tSCC zNeedsArg[] = "fixincl error:  `%s' needs %s argument (c_fix_arg[%d])\n";

#define EXIT_BROKEN  3

korbb's avatar
korbb committed
66 67 68 69 70 71
typedef struct {
    const char*  fix_name;
    void (*fix_proc)();
} fix_entry_t;

#define FIXUP_TABLE \
72
  _FT_( "char_macro_def",   char_macro_def_fix ) \
korbb's avatar
korbb committed
73 74 75 76
  _FT_( "char_macro_use",   char_macro_use_fix ) \
  _FT_( "format",           format_fix )         \
  _FT_( "machine_name",     machine_name_fix )   \
  _FT_( "wrap",             wrap_fix )
korbb's avatar
korbb committed
77 78 79


#define FIX_PROC_HEAD( fix ) \
80
static void fix ( filname, text, p_fixd ) \
korbb's avatar
korbb committed
81
    const char* filname; \
korbb's avatar
korbb committed
82
    const char* text; \
83
    tFixDesc* p_fixd;
korbb's avatar
korbb committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127


/*
 *  Skip over a quoted string.  Single quote strings may
 *  contain multiple characters if the first character is
 *  a backslash.  Especially a backslash followed by octal digits.
 *  We are not doing a correctness syntax check here.
 */
static char*
print_quote( q, text )
  char  q;
  char* text;
{
  fputc( q, stdout );

  for (;;)
    {
      char ch = *(text++);
      fputc( ch, stdout );

      switch (ch)
        {
        case '\\':
          if (*text == NUL)
            goto quote_done;

          fputc( *(text++), stdout );
          break;

        case '"':
        case '\'':
          if (ch != q)
            break;
          /*FALLTHROUGH*/

        case '\n':
        case NUL:
          goto quote_done;
        }
    } quote_done:;

  return text;
}

128 129 130 131 132 133 134 135

/*
 *  Copy the `format' string to std out, replacing `%n' expressions
 *  with the matched text from a regular expression evaluation.
 *  Doubled '%' characters will be replaced with a single copy.
 *  '%' characters in other contexts and all other characters are
 *  copied out verbatim.
 */
136 137 138 139 140 141
static void
format_write (format, text, av)
     tCC* format;
     tCC* text;
     regmatch_t av[];
{
142
  int c;
korbb's avatar
korbb committed
143

144
  while ((c = (unsigned)*(format++)) != NUL) {
korbb's avatar
korbb committed
145

146 147 148 149 150
    if (c != '%')
      {
        putchar(c);
        continue;
      }
korbb's avatar
korbb committed
151

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    c = (unsigned)*(format++);

    /*
     *  IF the character following a '%' is not a digit,
     *  THEN we will always emit a '%' and we may or may
     *  not emit the following character.  We will end on
     *  a NUL and we will emit only one of a pair of '%'.
     */
    if (! isdigit( c ))
      {
        putchar( '%' );
        switch (c) {
        case NUL:
          return;
        case '%':
          break;
        default:
          putchar(c);
korbb's avatar
korbb committed
170
        }
171
      }
korbb's avatar
korbb committed
172

173 174 175 176 177 178 179
    /*
     *  Emit the matched subexpression numbered 'c'.
     *  IF, of course, there was such a match...
     */
    else {
      regmatch_t*  pRM = av + (c - (unsigned)'0');
      size_t len;
korbb's avatar
korbb committed
180

181 182
      if (pRM->rm_so < 0)
        continue;
korbb's avatar
korbb committed
183

184 185 186
      len = pRM->rm_eo - pRM->rm_so;
      if (len > 0)
        fwrite(text + pRM->rm_so, len, 1, stdout);
187
    }
188
  }
189
}
korbb's avatar
korbb committed
190

korbb's avatar
korbb committed
191

192 193 194 195 196
/*
 *  Search for multiple copies of a regular expression.  Each block
 *  of matched text is replaced with the format string, as described
 *  above in `format_write'.
 */
197
FIX_PROC_HEAD( format_fix )
korbb's avatar
korbb committed
198
{
199 200 201 202 203 204 205 206 207 208 209
  tCC*  pz_pat = p_fixd->patch_args[2];
  tCC*  pz_fmt = p_fixd->patch_args[1];
  const char *p;
  regex_t re;
  regmatch_t rm[10];

  /*
   *  We must have a format
   */
  if (pz_fmt == (tCC*)NULL)
    {
210 211
      fprintf( stderr, zNeedsArg, p_fixd->fix_name, "replacement format", 0 );
      exit (EXIT_BROKEN);
212
    }
213

214 215 216 217 218 219 220 221 222 223 224 225
  /*
   *  IF we don't have a search text, then go find the first
   *  regular expression among the tests.
   */
  if (pz_pat == (tCC*)NULL)
    {
      tTestDesc* pTD = p_fixd->p_test_desc;
      int        ct  = p_fixd->test_ct;
      for (;;)
        {
          if (ct-- <= 0)
            {
226 227
              fprintf( stderr, zNeedsArg, p_fixd->fix_name, "search text", 1 );
              exit (EXIT_BROKEN);
228
            }
229

230 231 232 233
          if (pTD->type == TT_EGREP)
            {
              pz_pat = pTD->pz_test_text;
              break;
234 235
            }

236
          pTD++;
237
        }
238
    }
239

240 241 242 243 244
  /*
   *  Replace every copy of the text we find
   */
  compile_re (pz_pat, &re, 1, "format search-text", "format_fix" );
  while (regexec (&re, text, 10, rm, 0) == 0)
245
    {
246 247
      char* apz[10];
      int   i;
248

249 250 251
      fwrite( text, rm[0].rm_so, 1, stdout );
      format_write( pz_fmt, text, rm );
      text += rm[0].rm_eo;
252
    }
253

254 255 256 257
  /*
   *  Dump out the rest of the file
   */
  fputs (text, stdout);
258 259
}

korbb's avatar
korbb committed
260

261 262 263 264 265 266 267 268 269 270
/* Scan the input file for all occurrences of text like this:

   #define TIOCCONS _IO(T, 12)

   and change them to read like this:

   #define TIOCCONS _IO('T', 12)

   which is the required syntax per the C standard.  (The definition of
   _IO also has to be tweaked - see below.)  'IO' is actually whatever you
271
   provide as the `c_fix_arg' argument.  */
272 273

FIX_PROC_HEAD( char_macro_use_fix )
274 275 276
{
  /* This regexp looks for a traditional-syntax #define (# in column 1)
     of an object-like macro.  */
277 278
  static const char pat[] =
    "^#[ \t]*define[ \t]+[_A-Za-z][_A-Za-z0-9]*[ \t]+";
279 280
  static regex_t re;

281 282 283 284
  const char* str = p_fixd->patch_args[1];
  regmatch_t rm[1];
  const char *p, *limit;
  size_t len;
285

286
  if (str == NULL)
287
    {
288 289
      fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0);
      exit (EXIT_BROKEN);
290
    }
291

292 293
  len = strlen (str);
  compile_re (pat, &re, 1, "macro pattern", "char_macro_use_fix");
294

295 296 297
  for (p = text;
       regexec (&re, p, 1, rm, 0) == 0;
       p = limit + 1)
298
    {
299 300 301 302 303 304 305 306 307 308 309 310
      /* p + rm[0].rm_eo is the first character of the macro replacement.
	 Find the end of the macro replacement, and the STR we were
	 sent to look for within the replacement.  */
      p += rm[0].rm_eo;
      limit = p - 1;
      do
	{
	  limit = strchr (limit + 1, '\n');
	  if (!limit)
	    goto done;
	}
      while (limit[-1] == '\\');
311

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
      do
	{
	  if (*p == str[0] && !strncmp (p+1, str+1, len-1))
	    goto found;
	}
      while (++p < limit - len);
      /* Hit end of line.  */
      continue;

    found:
      /* Found STR on this line.  If the macro needs fixing,
	 the next few chars will be whitespace or uppercase,
	 then an open paren, then a single letter.  */
      while ((isspace (*p) || isupper (*p)) && p < limit) p++;
      if (*p++ != '(')
	continue;
      if (!isalpha (*p))
	continue;
      if (isalnum (p[1]) || p[1] == '_')
	continue;

      /* Splat all preceding text into the output buffer,
	 quote the character at p, then proceed.  */
      fwrite (text, 1, p - text, stdout);
      putchar ('\'');
      putchar (*p);
      putchar ('\'');
      text = p + 1;
    }
 done:
342 343 344
  fputs (text, stdout);
}

345

346 347
/* Scan the input file for all occurrences of text like this:

348
   #define xxxIOxx(x, y) (....'x'<<16....)
349 350 351

   and change them to read like this:

352
   #define xxxIOxx(x, y) (....x<<16....)
353 354

   which is the required syntax per the C standard.  (The uses of _IO
355 356
   also has to be tweaked - see above.)  'IO' is actually whatever
   you provide as the `c_fix_arg' argument.  */
357
FIX_PROC_HEAD( char_macro_def_fix )
358
{
359 360 361
  /* This regexp looks for any traditional-syntax #define (# in column 1).  */
  static const char pat[] =
    "^#[ \t]*define[ \t]+";
362 363
  static regex_t re;

364 365 366 367 368
  const char* str = p_fixd->patch_args[1];
  regmatch_t rm[1];
  const char *p, *limit;
  char arg;
  size_t len;
369

370
  if (str == NULL)
371
    {
372 373
      fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0);
      exit (EXIT_BROKEN);
374 375
    }

376 377
  len = strlen (str);
  compile_re (pat, &re, 1, "macro pattern", "fix_char_macro_defines");
378

379 380 381
  for (p = text;
       regexec (&re, p, 1, rm, 0) == 0;
       p = limit + 1)
382
    {
383 384 385 386 387 388 389 390 391 392 393 394
      /* p + rm[0].rm_eo is the first character of the macro name.
	 Find the end of the macro replacement, and the STR we were
	 sent to look for within the name.  */
      p += rm[0].rm_eo;
      limit = p - 1;
      do
	{
	  limit = strchr (limit + 1, '\n');
	  if (!limit)
	    goto done;
	}
      while (limit[-1] == '\\');
395

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
      do
	{
	  if (*p == str[0] && !strncmp (p+1, str+1, len-1))
	    goto found;
	  p++;
	}
      while (isalpha (*p) || isalnum (*p) || *p == '_');
      /* Hit end of macro name without finding the string.  */
      continue;

    found:
      /* Found STR in this macro name.  If the macro needs fixing,
	 there may be a few uppercase letters, then there will be an
	 open paren with _no_ intervening whitespace, and then a
	 single letter.  */
      while (isupper (*p) && p < limit) p++;
      if (*p++ != '(')
	continue;
      if (!isalpha (*p))
	continue;
      if (isalnum (p[1]) || p[1] == '_')
	continue;

      /* The character at P is the one to look for in the following
	 text.  */
      arg = *p;
      p += 2;

      while (p < limit)
	{
	  if (p[-1] == '\'' && p[0] == arg && p[1] == '\'')
	    {
	      /* Remove the quotes from this use of ARG.  */
	      p--;
	      fwrite (text, 1, p - text, stdout);
	      putchar (arg);
	      p += 3;
	      text = p;
	    }
	  else
	    p++;
	}
438
    }
439
 done:
440 441 442
  fputs (text, stdout);
}

443 444 445 446 447 448 449 450 451 452
/* Fix for machine name #ifdefs that are not in the namespace reserved
   by the C standard.  They won't be defined if compiling with -ansi,
   and the headers will break.  We go to some trouble to only change
   #ifdefs where the macro is defined by GCC in non-ansi mode; this
   minimizes the number of headers touched.  */

#define SCRATCHSZ 64   /* hopefully long enough */

FIX_PROC_HEAD( machine_name_fix )
{
korbb's avatar
korbb committed
453 454 455
#ifndef MN_NAME_PAT
  fputs( "The target machine has no needed machine name fixes\n", stderr );
#else
456
  regmatch_t match[2];
457
  const char *line, *base, *limit, *p, *q;
458 459 460 461
  regex_t *label_re, *name_re;
  char scratch[SCRATCHSZ];
  size_t len;

korbb's avatar
korbb committed
462
  mn_get_regexps (&label_re, &name_re, "machine_name_fix");
463

464 465 466 467 468 469 470 471 472
  scratch[0] = '_';
  scratch[1] = '_';

  for (base = text;
       regexec (label_re, base, 2, match, 0) == 0;
       base = limit)
    {
      base += match[0].rm_eo;
      /* We're looking at an #if or #ifdef.  Scan forward for the
473
         next non-escaped newline.  */
474 475
      line = limit = base;
      do
476 477 478 479 480 481
        {
          limit++;
          limit = strchr (limit, '\n');
          if (!limit)
            goto done;
        }
482 483 484
      while (limit[-1] == '\\');

      /* If the 'name_pat' matches in between base and limit, we have
485 486 487
         a bogon.  It is not worth the hassle of excluding comments
         because comments on #if/#ifdef lines are rare, and strings on
         such lines are illegal.
488

489 490 491
         REG_NOTBOL means 'base' is not at the beginning of a line, which
         shouldn't matter since the name_re has no ^ anchor, but let's
         be accurate anyway.  */
492 493

      for (;;)
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
        {
        again:
          if (base == limit)
            break;

          if (regexec (name_re, base, 1, match, REG_NOTBOL))
            goto done;  /* No remaining match in this file */

          /* Match; is it on the line?  */
          if (match[0].rm_eo > limit - base)
            break;

          p = base + match[0].rm_so;
          base += match[0].rm_eo;

          /* One more test: if on the same line we have the same string
             with the appropriate underscores, then leave it alone.
             We want exactly two leading and trailing underscores.  */
          if (*p == '_')
            {
              len = base - p - ((*base == '_') ? 2 : 1);
              q = p + 1;
            }
          else
            {
              len = base - p - ((*base == '_') ? 1 : 0);
              q = p;
            }
          if (len + 4 > SCRATCHSZ)
            abort ();
          memcpy (&scratch[2], q, len);
          len += 2;
          scratch[len++] = '_';
          scratch[len++] = '_';

          for (q = line; q <= limit - len; q++)
            if (*q == '_' && !strncmp (q, scratch, len))
              goto again;
          
          fwrite (text, 1, p - text, stdout);
          fwrite (scratch, 1, len, stdout);

          text = base;
        }
538 539
    }
 done:
korbb's avatar
korbb committed
540
#endif
541 542 543 544
  fputs (text, stdout);
}


korbb's avatar
korbb committed
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
FIX_PROC_HEAD( wrap_fix )
{
  char   z_fixname[ 64 ];
  tCC*   pz_src  = p_fixd->fix_name;
  tCC*   pz_name = z_fixname;
  char*  pz_dst  = z_fixname;
  size_t len     = 0;

  for (;;) {
    char ch = *(pz_src++);

    if (islower(ch))
      *(pz_dst++) = toupper( ch );

    else if (isalnum( ch ))
      *(pz_dst++) = ch;

    else if (ch == NUL) {
      *(pz_dst++) = ch;
      break;
    }
    else
      *(pz_dst++) = '_';

    if (++len >= sizeof( z_fixname )) {
570
      void* p = xmalloc( len + strlen( pz_src ) + 1 );
korbb's avatar
korbb committed
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
      memcpy( p, (void*)z_fixname, len );
      pz_name = (tCC*)p;
      pz_dst  = (char*)pz_name + len;
    }
  }

  printf( "#ifndef FIXINC_%s_CHECK\n", pz_name );
  printf( "#define FIXINC_%s_CHECK 1\n\n", pz_name );

  if (p_fixd->patch_args[1] == (tCC*)NULL)
    fputs( text, stdout );

  else {
    fputs( p_fixd->patch_args[1], stdout );
    fputs( text, stdout );
    if (p_fixd->patch_args[2] != (tCC*)NULL)
      fputs( p_fixd->patch_args[2], stdout );
  }

  printf( "\n#endif  /* FIXINC_%s_CHECK */\n", pz_name );
  if (pz_name != z_fixname)
    free( (void*)pz_name );
}


korbb's avatar
korbb committed
596 597 598 599 600 601 602 603
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

     test for fix selector

     THIS IS THE ONLY EXPORTED ROUTINE

*/
void
604 605 606
apply_fix( p_fixd, filname )
  tFixDesc* p_fixd;
  tCC* filname;
korbb's avatar
korbb committed
607
{
608
#define _FT_(n,p) { n, p },
korbb's avatar
korbb committed
609
  static fix_entry_t fix_table[] = { FIXUP_TABLE { NULL, NULL }};
610 611
#undef _FT_
#define FIX_TABLE_CT ((sizeof(fix_table)/sizeof(fix_table[0]))-1)
korbb's avatar
korbb committed
612

613
  tCC* fixname = p_fixd->patch_args[0];
korbb's avatar
korbb committed
614 615 616 617 618 619 620 621 622
  char* buf;
  int ct = FIX_TABLE_CT;
  fix_entry_t* pfe = fix_table;

  for (;;)
    {
      if (strcmp (pfe->fix_name, fixname) == 0)
        break;
      if (--ct <= 0)
623
        {
624
          fprintf (stderr, "fixincl error:  the `%s' fix is unknown\n",
625
                   fixname );
626
          exit (EXIT_BROKEN);
627 628
        }
      pfe++;
korbb's avatar
korbb committed
629 630 631
    }

  buf = load_file_data (stdin);
632
  (*pfe->fix_proc)( filname, buf, p_fixd );
korbb's avatar
korbb committed
633
}