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, 2000, 2001, 2003 7 Free Software Foundation, Inc. 8 9= = = = = = = = = = = = = = = = = = = = = = = = = 10 11NOTE TO DEVELOPERS 12 13The routines you write here must work closely with fixincl.c. 14 15Here are the rules: 16 171. Every test procedure name must be suffixed with "_fix". 18 These routines will be referenced from inclhack.def, sans the suffix. 19 202. 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 233. Put your test name into the FIXUP_TABLE. 24 254. Do not read anything from stdin. It is closed. 26 275. Write to stderr only in the event of a reportable error 28 In such an event, call "exit (EXIT_FAILURE)". 29 306. You have access to the fixDescList entry for the fix in question. 31 This may be useful, for example, if there are interesting strings 32 or pre-compiled regular expressions stored there. 33 34= = = = = = = = = = = = = = = = = = = = = = = = = 35 36This file is part of GCC. 37 38GCC is free software; you can redistribute it and/or modify 39it under the terms of the GNU General Public License as published by 40the Free Software Foundation; either version 2, or (at your option) 41any later version. 42 43GCC is distributed in the hope that it will be useful, 44but WITHOUT ANY WARRANTY; without even the implied warranty of 45MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 46GNU General Public License for more details. 47 48You should have received a copy of the GNU General Public License 49along with GCC; see the file COPYING. If not, write to 50the Free Software Foundation, 59 Temple Place - Suite 330, 51Boston, MA 02111-1307, USA. */ 52 53#include "fixlib.h" 54#define GTYPE_SE_CT 1 55 56#ifdef SEPARATE_FIX_PROC 57#include "fixincl.x" 58#endif 59 60void fatal (const char *s, ...) 61{ 62 fprintf (stderr, "%s: %s\n", s); 63 exit (1); 64} 65 66tSCC zNeedsArg[] = "fixincl error: `%s' needs %s argument (c_fix_arg[%d])\n"; 67 68typedef void t_fix_proc (const char *, const char *, tFixDesc *) ; 69typedef struct { 70 const char* fix_name; 71 t_fix_proc* fix_proc; 72} fix_entry_t; 73 74#define FIXUP_TABLE \ 75 _FT_( "char_macro_def", char_macro_def_fix ) \ 76 _FT_( "char_macro_use", char_macro_use_fix ) \ 77 _FT_( "format", format_fix ) \ 78 _FT_( "machine_name", machine_name_fix ) \ 79 _FT_( "wrap", wrap_fix ) \ 80 _FT_( "gnu_type", gnu_type_fix ) 81 82 83#define FIX_PROC_HEAD( fix ) \ 84static void fix (const char* filname ATTRIBUTE_UNUSED , \ 85 const char* text ATTRIBUTE_UNUSED , \ 86 tFixDesc* p_fixd ATTRIBUTE_UNUSED ) 87 88#ifdef NEED_PRINT_QUOTE 89/* 90 * Skip over a quoted string. Single quote strings may 91 * contain multiple characters if the first character is 92 * a backslash. Especially a backslash followed by octal digits. 93 * We are not doing a correctness syntax check here. 94 */ 95static char* 96print_quote(char q, char* text ) 97{ 98 fputc( q, stdout ); 99 100 for (;;) 101 { 102 char ch = *(text++); 103 fputc( ch, stdout ); 104 105 switch (ch) 106 { 107 case '\\': 108 if (*text == NUL) 109 goto quote_done; 110 111 fputc( *(text++), stdout ); 112 break; 113 114 case '"': 115 case '\'': 116 if (ch != q) 117 break; 118 /*FALLTHROUGH*/ 119 120 case '\n': 121 case NUL: 122 goto quote_done; 123 } 124 } quote_done:; 125 126 return text; 127} 128#endif /* NEED_PRINT_QUOTE */ 129 130 131/* 132 * Emit the GNU standard type wrapped up in such a way that 133 * this thing can be encountered countless times during a compile 134 * and not cause even a warning. 135 */ 136static const char* 137emit_gnu_type (const char* text, regmatch_t* rm ) 138{ 139 char z_TYPE[ 64 ]; 140 char z_type[ 64 ]; 141 142 fwrite (text, rm[0].rm_so, 1, stdout); 143 144 { 145 const char* ps = text + rm[1].rm_so; 146 const char* pe = text + rm[1].rm_eo; 147 char* pd = z_type; 148 char* pD = z_TYPE; 149 150 while (ps < pe) 151 *(pD++) = TOUPPER( *(pd++) = *(ps++) ); 152 153 *pD = *pd = NUL; 154 } 155 156 /* 157 * Now print out the reformed typedef, 158 * with a C++ guard for WCHAR 159 */ 160 { 161 tSCC z_fmt[] = "\ 162#if !defined(_GCC_%s_T)%s\n\ 163#define _GCC_%s_T\n\ 164typedef __%s_TYPE__ %s_t;\n\ 165#endif\n"; 166 167 const char *const pz_guard = (strcmp (z_type, "wchar") == 0) 168 ? " && ! defined(__cplusplus)" : ""; 169 170 printf (z_fmt, z_TYPE, pz_guard, z_TYPE, z_TYPE, z_type); 171 } 172 173 return text += rm[0].rm_eo; 174} 175 176 177/* 178 * Copy the `format' string to std out, replacing `%n' expressions 179 * with the matched text from a regular expression evaluation. 180 * Doubled '%' characters will be replaced with a single copy. 181 * '%' characters in other contexts and all other characters are 182 * copied out verbatim. 183 */ 184static void 185format_write (tCC* format, tCC* text, regmatch_t av[] ) 186{ 187 int c; 188 189 while ((c = (unsigned)*(format++)) != NUL) { 190 191 if (c != '%') 192 { 193 putchar(c); 194 continue; 195 } 196 197 c = (unsigned)*(format++); 198 199 /* 200 * IF the character following a '%' is not a digit, 201 * THEN we will always emit a '%' and we may or may 202 * not emit the following character. We will end on 203 * a NUL and we will emit only one of a pair of '%'. 204 */ 205 if (! ISDIGIT ( c )) 206 { 207 putchar( '%' ); 208 switch (c) { 209 case NUL: 210 return; 211 case '%': 212 break; 213 default: 214 putchar(c); 215 } 216 } 217 218 /* 219 * Emit the matched subexpression numbered 'c'. 220 * IF, of course, there was such a match... 221 */ 222 else { 223 regmatch_t* pRM = av + (c - (unsigned)'0'); 224 size_t len; 225 226 if (pRM->rm_so < 0) 227 continue; 228 229 len = pRM->rm_eo - pRM->rm_so; 230 if (len > 0) 231 fwrite(text + pRM->rm_so, len, 1, stdout); 232 } 233 } 234} 235 236 237/* 238 * Search for multiple copies of a regular expression. Each block 239 * of matched text is replaced with the format string, as described 240 * above in `format_write'. 241 */ 242FIX_PROC_HEAD( format_fix ) 243{ 244 tCC* pz_pat = p_fixd->patch_args[2]; 245 tCC* pz_fmt = p_fixd->patch_args[1]; 246 regex_t re; 247 regmatch_t rm[10]; 248 IGNORE_ARG(filname); 249 250 /* 251 * We must have a format 252 */ 253 if (pz_fmt == (tCC*)NULL) 254 { 255 fprintf( stderr, zNeedsArg, p_fixd->fix_name, "replacement format", 0 ); 256 exit (EXIT_BROKEN); 257 } 258 259 /* 260 * IF we don't have a search text, then go find the first 261 * regular expression among the tests. 262 */ 263 if (pz_pat == (tCC*)NULL) 264 { 265 tTestDesc* pTD = p_fixd->p_test_desc; 266 int ct = p_fixd->test_ct; 267 for (;;) 268 { 269 if (ct-- <= 0) 270 { 271 fprintf( stderr, zNeedsArg, p_fixd->fix_name, "search text", 1 ); 272 exit (EXIT_BROKEN); 273 } 274 275 if (pTD->type == TT_EGREP) 276 { 277 pz_pat = pTD->pz_test_text; 278 break; 279 } 280 281 pTD++; 282 } 283 } 284 285 /* 286 * Replace every copy of the text we find 287 */ 288 compile_re (pz_pat, &re, 1, "format search-text", "format_fix" ); 289 while (xregexec (&re, text, 10, rm, 0) == 0) 290 { 291 fwrite( text, rm[0].rm_so, 1, stdout ); 292 format_write( pz_fmt, text, rm ); 293 text += rm[0].rm_eo; 294 } 295 296 /* 297 * Dump out the rest of the file 298 */ 299 fputs (text, stdout); 300} 301 302 303/* Scan the input file for all occurrences of text like this: 304 305 #define TIOCCONS _IO(T, 12) 306 307 and change them to read like this: 308 309 #define TIOCCONS _IO('T', 12) 310 311 which is the required syntax per the C standard. (The definition of 312 _IO also has to be tweaked - see below.) 'IO' is actually whatever you 313 provide as the `c_fix_arg' argument. */ 314 315FIX_PROC_HEAD( char_macro_use_fix ) 316{ 317 /* This regexp looks for a traditional-syntax #define (# in column 1) 318 of an object-like macro. */ 319 static const char pat[] = 320 "^#[ \t]*define[ \t]+[_A-Za-z][_A-Za-z0-9]*[ \t]+"; 321 static regex_t re; 322 323 const char* str = p_fixd->patch_args[1]; 324 regmatch_t rm[1]; 325 const char *p, *limit; 326 size_t len; 327 IGNORE_ARG(filname); 328 329 if (str == NULL) 330 { 331 fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0); 332 exit (EXIT_BROKEN); 333 } 334 335 len = strlen (str); 336 compile_re (pat, &re, 1, "macro pattern", "char_macro_use_fix"); 337 338 for (p = text; 339 xregexec (&re, p, 1, rm, 0) == 0; 340 p = limit + 1) 341 { 342 /* p + rm[0].rm_eo is the first character of the macro replacement. 343 Find the end of the macro replacement, and the STR we were 344 sent to look for within the replacement. */ 345 p += rm[0].rm_eo; 346 limit = p - 1; 347 do 348 { 349 limit = strchr (limit + 1, '\n'); 350 if (!limit) 351 goto done; 352 } 353 while (limit[-1] == '\\'); 354 355 do 356 { 357 if (*p == str[0] && !strncmp (p+1, str+1, len-1)) 358 goto found; 359 } 360 while (++p < limit - len); 361 /* Hit end of line. */ 362 continue; 363 364 found: 365 /* Found STR on this line. If the macro needs fixing, 366 the next few chars will be whitespace or uppercase, 367 then an open paren, then a single letter. */ 368 while ((ISSPACE (*p) || ISUPPER (*p)) && p < limit) p++; 369 if (*p++ != '(') 370 continue; 371 if (!ISALPHA (*p)) 372 continue; 373 if (ISIDNUM (p[1])) 374 continue; 375 376 /* Splat all preceding text into the output buffer, 377 quote the character at p, then proceed. */ 378 fwrite (text, 1, p - text, stdout); 379 putchar ('\''); 380 putchar (*p); 381 putchar ('\''); 382 text = p + 1; 383 } 384 done: 385 fputs (text, stdout); 386} 387 388 389/* Scan the input file for all occurrences of text like this: 390 391 #define xxxIOxx(x, y) (....'x'<<16....) 392 393 and change them to read like this: 394 395 #define xxxIOxx(x, y) (....x<<16....) 396 397 which is the required syntax per the C standard. (The uses of _IO 398 also has to be tweaked - see above.) 'IO' is actually whatever 399 you provide as the `c_fix_arg' argument. */ 400FIX_PROC_HEAD( char_macro_def_fix ) 401{ 402 /* This regexp looks for any traditional-syntax #define (# in column 1). */ 403 static const char pat[] = 404 "^#[ \t]*define[ \t]+"; 405 static regex_t re; 406 407 const char* str = p_fixd->patch_args[1]; 408 regmatch_t rm[1]; 409 const char *p, *limit; 410 char arg; 411 size_t len; 412 IGNORE_ARG(filname); 413 414 if (str == NULL) 415 { 416 fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0); 417 exit (EXIT_BROKEN); 418 } 419 420 len = strlen (str); 421 compile_re (pat, &re, 1, "macro pattern", "fix_char_macro_defines"); 422 423 for (p = text; 424 xregexec (&re, p, 1, rm, 0) == 0; 425 p = limit + 1) 426 { 427 /* p + rm[0].rm_eo is the first character of the macro name. 428 Find the end of the macro replacement, and the STR we were 429 sent to look for within the name. */ 430 p += rm[0].rm_eo; 431 limit = p - 1; 432 do 433 { 434 limit = strchr (limit + 1, '\n'); 435 if (!limit) 436 goto done; 437 } 438 while (limit[-1] == '\\'); 439 440 do 441 { 442 if (*p == str[0] && !strncmp (p+1, str+1, len-1)) 443 goto found; 444 p++; 445 } 446 while (ISIDNUM (*p)); 447 /* Hit end of macro name without finding the string. */ 448 continue; 449 450 found: 451 /* Found STR in this macro name. If the macro needs fixing, 452 there may be a few uppercase letters, then there will be an 453 open paren with _no_ intervening whitespace, and then a 454 single letter. */ 455 while (ISUPPER (*p) && p < limit) p++; 456 if (*p++ != '(') 457 continue; 458 if (!ISALPHA (*p)) 459 continue; 460 if (ISIDNUM (p[1])) 461 continue; 462 463 /* The character at P is the one to look for in the following 464 text. */ 465 arg = *p; 466 p += 2; 467 468 while (p < limit) 469 { 470 if (p[-1] == '\'' && p[0] == arg && p[1] == '\'') 471 { 472 /* Remove the quotes from this use of ARG. */ 473 p--; 474 fwrite (text, 1, p - text, stdout); 475 putchar (arg); 476 p += 3; 477 text = p; 478 } 479 else 480 p++; 481 } 482 } 483 done: 484 fputs (text, stdout); 485} 486 487/* Fix for machine name #ifdefs that are not in the namespace reserved 488 by the C standard. They won't be defined if compiling with -ansi, 489 and the headers will break. We go to some trouble to only change 490 #ifdefs where the macro is defined by GCC in non-ansi mode; this 491 minimizes the number of headers touched. */ 492 493#define SCRATCHSZ 64 /* hopefully long enough */ 494 495FIX_PROC_HEAD( machine_name_fix ) 496{ 497#ifndef MN_NAME_PAT 498 fputs( "The target machine has no needed machine name fixes\n", stderr ); 499#else 500 regmatch_t match[2]; 501 const char *line, *base, *limit, *p, *q; 502 regex_t *label_re, *name_re; 503 char scratch[SCRATCHSZ]; 504 size_t len; 505 IGNORE_ARG(filname); 506 IGNORE_ARG(p_fixd); 507 508 mn_get_regexps (&label_re, &name_re, "machine_name_fix"); 509 510 scratch[0] = '_'; 511 scratch[1] = '_'; 512 513 for (base = text; 514 xregexec (label_re, base, 2, match, 0) == 0; 515 base = limit) 516 { 517 base += match[0].rm_eo; 518 /* We're looking at an #if or #ifdef. Scan forward for the 519 next non-escaped newline. */ 520 line = limit = base; 521 do 522 { 523 limit++; 524 limit = strchr (limit, '\n'); 525 if (!limit) 526 goto done; 527 } 528 while (limit[-1] == '\\'); 529 530 /* If the 'name_pat' matches in between base and limit, we have 531 a bogon. It is not worth the hassle of excluding comments 532 because comments on #if/#ifdef lines are rare, and strings on 533 such lines are illegal. 534 535 REG_NOTBOL means 'base' is not at the beginning of a line, which 536 shouldn't matter since the name_re has no ^ anchor, but let's 537 be accurate anyway. */ 538 539 for (;;) 540 { 541 again: 542 if (base == limit) 543 break; 544 545 if (xregexec (name_re, base, 1, match, REG_NOTBOL)) 546 goto done; /* No remaining match in this file */ 547 548 /* Match; is it on the line? */ 549 if (match[0].rm_eo > limit - base) 550 break; 551 552 p = base + match[0].rm_so; 553 base += match[0].rm_eo; 554 555 /* One more test: if on the same line we have the same string 556 with the appropriate underscores, then leave it alone. 557 We want exactly two leading and trailing underscores. */ 558 if (*p == '_') 559 { 560 len = base - p - ((*base == '_') ? 2 : 1); 561 q = p + 1; 562 } 563 else 564 { 565 len = base - p - ((*base == '_') ? 1 : 0); 566 q = p; 567 } 568 if (len + 4 > SCRATCHSZ) 569 abort (); 570 memcpy (&scratch[2], q, len); 571 len += 2; 572 scratch[len++] = '_'; 573 scratch[len++] = '_'; 574 575 for (q = line; q <= limit - len; q++) 576 if (*q == '_' && !strncmp (q, scratch, len)) 577 goto again; 578 579 fwrite (text, 1, p - text, stdout); 580 fwrite (scratch, 1, len, stdout); 581 582 text = base; 583 } 584 } 585 done: 586#endif 587 fputs (text, stdout); 588} 589 590 591FIX_PROC_HEAD( wrap_fix ) 592{ 593 tSCC z_no_wrap_pat[] = "^#if.*__need_"; 594 static regex_t no_wrapping_re; /* assume zeroed data */ 595 596 tCC* pz_name = NULL; 597 598 if (no_wrapping_re.allocated == 0) 599 compile_re( z_no_wrap_pat, &no_wrapping_re, 0, "no-wrap pattern", 600 "wrap-fix" ); 601 602 /* 603 * IF we do *not* match the no-wrap re, then we have a double negative. 604 * A double negative means YES. 605 */ 606 if (xregexec( &no_wrapping_re, text, 0, NULL, 0 ) != 0) 607 { 608 /* 609 * A single file can get wrapped more than once by different fixes. 610 * A single fix can wrap multiple files. Therefore, guard with 611 * *both* the fix name and the file name. 612 */ 613 size_t ln = strlen( filname ) + strlen( p_fixd->fix_name ) + 14; 614 char* pz = xmalloc( ln ); 615 pz_name = pz; 616 sprintf( pz, "FIXINC_WRAP_%s-%s", filname, p_fixd->fix_name ); 617 618 for (pz += 12; 1; pz++) { 619 char ch = *pz; 620 621 if (ch == NUL) 622 break; 623 624 if (! ISALNUM( ch )) { 625 *pz = '_'; 626 } 627 else { 628 *pz = TOUPPER( ch ); 629 } 630 } 631 632 printf( "#ifndef %s\n", pz_name ); 633 printf( "#define %s 1\n\n", pz_name ); 634 } 635 636 if (p_fixd->patch_args[1] == (tCC*)NULL) 637 fputs( text, stdout ); 638 639 else { 640 fputs( p_fixd->patch_args[1], stdout ); 641 fputs( text, stdout ); 642 if (p_fixd->patch_args[2] != (tCC*)NULL) 643 fputs( p_fixd->patch_args[2], stdout ); 644 } 645 646 if (pz_name != NULL) { 647 printf( "\n#endif /* %s */\n", pz_name ); 648 free( (void*)pz_name ); 649 } 650} 651 652 653/* 654 * Search for multiple copies of a regular expression. Each block 655 * of matched text is replaced with the format string, as described 656 * above in `format_write'. 657 */ 658FIX_PROC_HEAD( gnu_type_fix ) 659{ 660 const char* pz_pat; 661 regex_t re; 662 regmatch_t rm[GTYPE_SE_CT+1]; 663 IGNORE_ARG(filname); 664 665 { 666 tTestDesc* pTD = p_fixd->p_test_desc; 667 int ct = p_fixd->test_ct; 668 for (;;) 669 { 670 if (ct-- <= 0) 671 { 672 fprintf (stderr, zNeedsArg, p_fixd->fix_name, "search text", 1); 673 exit (EXIT_BROKEN); 674 } 675 676 if (pTD->type == TT_EGREP) 677 { 678 pz_pat = pTD->pz_test_text; 679 break; 680 } 681 682 pTD++; 683 } 684 } 685 686 compile_re (pz_pat, &re, 1, "gnu type typedef", "gnu_type_fix"); 687 688 while (xregexec (&re, text, GTYPE_SE_CT+1, rm, 0) == 0) 689 { 690 text = emit_gnu_type (text, rm); 691 } 692 693 /* 694 * Dump out the rest of the file 695 */ 696 fputs (text, stdout); 697} 698 699 700/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 701 702 test for fix selector 703 704 THIS IS THE ONLY EXPORTED ROUTINE 705 706*/ 707void 708apply_fix( tFixDesc* p_fixd, tCC* filname ) 709{ 710#define _FT_(n,p) { n, p }, 711 static fix_entry_t fix_table[] = { FIXUP_TABLE { NULL, NULL }}; 712#undef _FT_ 713#define FIX_TABLE_CT (ARRAY_SIZE (fix_table)-1) 714 715 tCC* fixname = p_fixd->patch_args[0]; 716 char* buf; 717 int ct = FIX_TABLE_CT; 718 fix_entry_t* pfe = fix_table; 719 720 for (;;) 721 { 722 if (strcmp (pfe->fix_name, fixname) == 0) 723 break; 724 if (--ct <= 0) 725 { 726 fprintf (stderr, "fixincl error: the `%s' fix is unknown\n", 727 fixname ); 728 exit (EXIT_BROKEN); 729 } 730 pfe++; 731 } 732 733 buf = load_file_data (stdin); 734 (*pfe->fix_proc)( filname, buf, p_fixd ); 735} 736 737#ifdef SEPARATE_FIX_PROC 738tSCC z_usage[] = 739"USAGE: applyfix <fix-name> <file-to-fix> <file-source> <file-destination>\n"; 740tSCC z_reopen[] = 741"FS error %d (%s) reopening %s as std%s\n"; 742 743int 744main( int argc, char** argv ) 745{ 746 tFixDesc* pFix; 747 char* pz_tmptmp; 748 char* pz_tmp_base; 749 char* pz_tmp_dot; 750 751 if (argc != 5) 752 { 753 usage_failure: 754 fputs (z_usage, stderr); 755 return EXIT_FAILURE; 756 } 757 758 { 759 char* pz = argv[1]; 760 long idx; 761 762 if (! ISDIGIT ( *pz )) 763 goto usage_failure; 764 765 idx = strtol (pz, &pz, 10); 766 if ((*pz != NUL) || ((unsigned)idx >= FIX_COUNT)) 767 goto usage_failure; 768 pFix = fixDescList + idx; 769 } 770 771 if (freopen (argv[3], "r", stdin) != stdin) 772 { 773 fprintf (stderr, z_reopen, errno, strerror( errno ), argv[3], "in"); 774 return EXIT_FAILURE; 775 } 776 777 pz_tmptmp = xmalloc (strlen (argv[4]) + 5); 778 strcpy( pz_tmptmp, argv[4] ); 779 780 /* Don't lose because "12345678" and "12345678X" map to the same 781 file under DOS restricted 8+3 file namespace. Note that DOS 782 doesn't allow more than one dot in the trunk of a file name. */ 783 pz_tmp_base = basename( pz_tmptmp ); 784 pz_tmp_dot = strchr( pz_tmp_base, '.' ); 785 if (pathconf( pz_tmptmp, _PC_NAME_MAX ) <= 12 /* is this DOS or Windows9X? */ 786 && pz_tmp_dot != (char*)NULL) 787 strcpy (pz_tmp_dot+1, "X"); /* nuke the original extension */ 788 else 789 strcat (pz_tmptmp, ".X"); 790 if (freopen (pz_tmptmp, "w", stdout) != stdout) 791 { 792 fprintf (stderr, z_reopen, errno, strerror( errno ), pz_tmptmp, "out"); 793 return EXIT_FAILURE; 794 } 795 796 apply_fix (pFix, argv[1]); 797 fclose (stdout); 798 fclose (stdin); 799 unlink (argv[4]); 800 if (rename (pz_tmptmp, argv[4]) != 0) 801 { 802 fprintf (stderr, "error %d (%s) renaming %s to %s\n", errno, 803 strerror( errno ), pz_tmptmp, argv[4]); 804 return EXIT_FAILURE; 805 } 806 807 return EXIT_SUCCESS; 808} 809#endif 810