1 /* Install modified versions of certain ANSI-incompatible system header 2 files which are fixed to work correctly with ANSI C and placed in a 3 directory that GCC will search. 4 5 Copyright (C) 1997, 1998, 1999, 2000, 2004 Free Software Foundation, Inc. 6 7 This file is part of GCC. 8 9 GCC is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 GCC is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with GCC; see the file COPYING. If not, write to 21 the Free Software Foundation, 51 Franklin Street, Fifth Floor, 22 Boston, MA 02110-1301, USA. */ 23 24 #include "fixlib.h" 25 26 #include <sys/stat.h> 27 #ifndef SEPARATE_FIX_PROC 28 #include <sys/wait.h> 29 #endif 30 31 #if defined( HAVE_MMAP_FILE ) 32 #include <sys/mman.h> 33 #define BAD_ADDR ((void*)-1) 34 #endif 35 36 #ifndef SEPARATE_FIX_PROC 37 #include "server.h" 38 #endif 39 40 /* The contents of this string are not very important. It is mostly 41 just used as part of the "I am alive and working" test. */ 42 43 static const char program_id[] = "fixincl version 1.1"; 44 45 /* This format will be used at the start of every generated file */ 46 47 static const char z_std_preamble[] = 48 "/* DO NOT EDIT THIS FILE.\n\n\ 49 It has been auto-edited by fixincludes from:\n\n\ 50 \t\"%s/%s\"\n\n\ 51 This had to be done to correct non-standard usages in the\n\ 52 original, manufacturer supplied header file. */\n\n"; 53 54 int find_base_len = 0; 55 56 typedef enum { 57 VERB_SILENT = 0, 58 VERB_FIXES, 59 VERB_APPLIES, 60 VERB_PROGRESS, 61 VERB_TESTS, 62 VERB_EVERYTHING 63 } te_verbose; 64 65 te_verbose verbose_level = VERB_PROGRESS; 66 int have_tty = 0; 67 68 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l) 69 #define NOT_SILENT VLEVEL(VERB_FIXES) 70 71 pid_t process_chain_head = (pid_t) -1; 72 73 char* pz_curr_file; /* name of the current file under test/fix */ 74 char* pz_curr_data; /* original contents of that file */ 75 char* pz_temp_file; /* for DOS, a place to stash the temporary 76 fixed data between system(3) calls */ 77 t_bool curr_data_mapped; 78 int data_map_fd; 79 size_t data_map_size; 80 size_t ttl_data_size = 0; 81 82 #ifdef DO_STATS 83 int process_ct = 0; 84 int apply_ct = 0; 85 int fixed_ct = 0; 86 int altered_ct = 0; 87 #endif /* DO_STATS */ 88 89 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]"; 90 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n"; 91 regex_t incl_quote_re; 92 93 static void do_version (void) ATTRIBUTE_NORETURN; 94 char *load_file (const char *); 95 void run_compiles (void); 96 void initialize (int argc, char** argv); 97 void process (void); 98 99 /* External Source Code */ 100 101 #include "fixincl.x" 102 103 /* * * * * * * * * * * * * * * * * * * 104 * 105 * MAIN ROUTINE 106 */ 107 extern int main (int, char **); 108 int 109 main (int argc, char** argv) 110 { 111 char *file_name_buf; 112 113 initialize ( argc, argv ); 114 115 have_tty = isatty (fileno (stderr)); 116 117 /* Before anything else, ensure we can allocate our file name buffer. */ 118 file_name_buf = load_file_data (stdin); 119 120 /* Because of the way server shells work, you have to keep stdin, out 121 and err open so that the proper input file does not get closed 122 by accident */ 123 124 freopen ("/dev/null", "r", stdin); 125 126 if (file_name_buf == (char *) NULL) 127 { 128 fputs ("No file names listed for fixing\n", stderr); 129 exit (EXIT_FAILURE); 130 } 131 132 for (;;) 133 { 134 char* pz_end; 135 136 /* skip to start of name, past any "./" prefixes */ 137 138 while (ISSPACE (*file_name_buf)) file_name_buf++; 139 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/')) 140 file_name_buf += 2; 141 142 /* Check for end of list */ 143 144 if (*file_name_buf == NUL) 145 break; 146 147 /* Set global file name pointer and find end of name */ 148 149 pz_curr_file = file_name_buf; 150 pz_end = strchr( pz_curr_file, '\n' ); 151 if (pz_end == (char*)NULL) 152 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file); 153 else 154 file_name_buf = pz_end + 1; 155 156 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--; 157 158 /* IF no name is found (blank line) or comment marker, skip line */ 159 160 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#')) 161 continue; 162 *pz_end = NUL; 163 164 process (); 165 } /* for (;;) */ 166 167 #ifdef DO_STATS 168 if (VLEVEL( VERB_PROGRESS )) { 169 tSCC zFmt[] = 170 "\ 171 Processed %5d files containing %d bytes \n\ 172 Applying %5d fixes to %d files\n\ 173 Altering %5d of them\n"; 174 175 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct, 176 fixed_ct, altered_ct); 177 } 178 #endif /* DO_STATS */ 179 180 # ifdef SEPARATE_FIX_PROC 181 unlink( pz_temp_file ); 182 # endif 183 exit (EXIT_SUCCESS); 184 } 185 186 187 static void 188 do_version (void) 189 { 190 static const char zFmt[] = "echo '%s'"; 191 char zBuf[ 1024 ]; 192 193 /* The 'version' option is really used to test that: 194 1. The program loads correctly (no missing libraries) 195 2. that we can compile all the regular expressions. 196 3. we can correctly run our server shell process 197 */ 198 run_compiles (); 199 sprintf (zBuf, zFmt, program_id); 200 #ifndef SEPARATE_FIX_PROC 201 puts (zBuf + 5); 202 exit (strcmp (run_shell (zBuf), program_id)); 203 #else 204 exit (system (zBuf)); 205 #endif 206 } 207 208 /* * * * * * * * * * * * */ 209 210 void 211 initialize ( int argc, char** argv ) 212 { 213 xmalloc_set_program_name (argv[0]); 214 215 switch (argc) 216 { 217 case 1: 218 break; 219 220 case 2: 221 if (strcmp (argv[1], "-v") == 0) 222 do_version (); 223 if (freopen (argv[1], "r", stdin) == (FILE*)NULL) 224 { 225 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n", 226 errno, xstrerror (errno), argv[1] ); 227 exit (EXIT_FAILURE); 228 } 229 break; 230 231 default: 232 fputs ("fixincl ERROR: too many command line arguments\n", stderr); 233 exit (EXIT_FAILURE); 234 } 235 236 #ifdef SIGCHLD 237 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will 238 receive the signal. A different setting is inheritable */ 239 signal (SIGCHLD, SIG_DFL); 240 #endif 241 242 initialize_opts (); 243 244 if (ISDIGIT ( *pz_verbose )) 245 verbose_level = (te_verbose)atoi( pz_verbose ); 246 else 247 switch (*pz_verbose) { 248 case 's': 249 case 'S': 250 verbose_level = VERB_SILENT; break; 251 252 case 'f': 253 case 'F': 254 verbose_level = VERB_FIXES; break; 255 256 case 'a': 257 case 'A': 258 verbose_level = VERB_APPLIES; break; 259 260 default: 261 case 'p': 262 case 'P': 263 verbose_level = VERB_PROGRESS; break; 264 265 case 't': 266 case 'T': 267 verbose_level = VERB_TESTS; break; 268 269 case 'e': 270 case 'E': 271 verbose_level = VERB_EVERYTHING; break; 272 } 273 if (verbose_level >= VERB_EVERYTHING) { 274 verbose_level = VERB_EVERYTHING; 275 fputs ("fixinc verbosity: EVERYTHING\n", stderr); 276 } 277 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/')) 278 pz_find_base += 2; 279 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL)) 280 find_base_len = strlen( pz_find_base ); 281 282 /* Compile all the regular expressions now. 283 That way, it is done only once for the whole run. 284 */ 285 run_compiles (); 286 287 # ifdef SEPARATE_FIX_PROC 288 /* NULL as the first argument to `tempnam' causes it to DTRT 289 wrt the temporary directory where the file will be created. */ 290 pz_temp_file = tempnam( NULL, "fxinc" ); 291 # endif 292 293 signal (SIGQUIT, SIG_IGN); 294 signal (SIGIOT, SIG_IGN); 295 signal (SIGPIPE, SIG_IGN); 296 signal (SIGALRM, SIG_IGN); 297 signal (SIGTERM, SIG_IGN); 298 } 299 300 /* * * * * * * * * * * * * 301 302 load_file loads all the contents of a file into malloc-ed memory. 303 Its argument is the name of the file to read in; the returned 304 result is the NUL terminated contents of the file. The file 305 is presumed to be an ASCII text file containing no NULs. */ 306 char * 307 load_file ( const char* fname ) 308 { 309 struct stat stbf; 310 char* res; 311 312 if (stat (fname, &stbf) != 0) 313 { 314 if (NOT_SILENT) 315 fprintf (stderr, "error %d (%s) stat-ing %s\n", 316 errno, xstrerror (errno), fname ); 317 return (char *) NULL; 318 } 319 if (stbf.st_size == 0) 320 return (char*)NULL; 321 322 /* Make the data map size one larger than the file size for documentation 323 purposes. Truth is that there will be a following NUL character if 324 the file size is not a multiple of the page size. If it is a multiple, 325 then this adjustment sometimes fails anyway. */ 326 data_map_size = stbf.st_size+1; 327 data_map_fd = open (fname, O_RDONLY); 328 ttl_data_size += data_map_size-1; 329 330 if (data_map_fd < 0) 331 { 332 if (NOT_SILENT) 333 fprintf (stderr, "error %d (%s) opening %s for read\n", 334 errno, xstrerror (errno), fname); 335 return (char*)NULL; 336 } 337 338 #ifdef HAVE_MMAP_FILE 339 curr_data_mapped = BOOL_TRUE; 340 341 /* IF the file size is a multiple of the page size, 342 THEN sometimes you will seg fault trying to access a trailing byte */ 343 if ((stbf.st_size & (getpagesize()-1)) == 0) 344 res = (char*)BAD_ADDR; 345 else 346 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ, 347 MAP_PRIVATE, data_map_fd, 0); 348 if (res == (char*)BAD_ADDR) 349 #endif 350 { 351 FILE* fp = fdopen (data_map_fd, "r"); 352 curr_data_mapped = BOOL_FALSE; 353 res = load_file_data (fp); 354 fclose (fp); 355 } 356 357 return res; 358 } 359 360 static int 361 machine_matches( tFixDesc* p_fixd ) 362 { 363 # ifndef SEPARATE_FIX_PROC 364 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */ 365 tSCC esac_fmt[] = 366 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */ 367 tSCC skip[] = "skip"; /* 4 bytes */ 368 tSCC run[] = "run"; /* 3 bytes */ 369 /* total bytes to add to machine sum: 49 - see fixincl.tpl */ 370 371 const char **papz_machs = p_fixd->papz_machs; 372 char *pz; 373 const char *pz_sep = ""; 374 tCC *pz_if_true; 375 tCC *pz_if_false; 376 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */ 377 378 /* Start the case statement */ 379 380 sprintf (cmd_buf, case_fmt, pz_machine); 381 pz = cmd_buf + strlen (cmd_buf); 382 383 /* Determine if a match means to apply the fix or not apply it */ 384 385 if (p_fixd->fd_flags & FD_MACH_IFNOT) 386 { 387 pz_if_true = skip; 388 pz_if_false = run; 389 } 390 else 391 { 392 pz_if_true = run; 393 pz_if_false = skip; 394 } 395 396 /* Emit all the machine names. If there are more than one, 397 then we will insert " | \\\n" between the names */ 398 399 for (;;) 400 { 401 const char* pz_mach = *(papz_machs++); 402 403 if (pz_mach == (const char*) NULL) 404 break; 405 sprintf (pz, "%s%s", pz_sep, pz_mach); 406 pz += strlen (pz); 407 pz_sep = " | \\\n"; 408 } 409 410 /* Now emit the match and not-match actions and the esac */ 411 412 sprintf (pz, esac_fmt, pz_if_true, pz_if_false); 413 414 /* Run the script. 415 The result will start either with 's' or 'r'. */ 416 417 { 418 int skip; 419 pz = run_shell (cmd_buf); 420 skip = (*pz == 's'); 421 free ( (void*)pz ); 422 if (skip) 423 { 424 p_fixd->fd_flags |= FD_SKIP_TEST; 425 return BOOL_FALSE; 426 } 427 } 428 429 return BOOL_TRUE; 430 # else /* is SEPARATE_FIX_PROC */ 431 const char **papz_machs = p_fixd->papz_machs; 432 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0; 433 for (;;) 434 { 435 const char* pz_mach = *(papz_machs++); 436 437 if (pz_mach == (const char*) NULL) 438 break; 439 if (strstr (pz_mach, "dos") != NULL && !invert) 440 return BOOL_TRUE; 441 } 442 443 p_fixd->fd_flags |= FD_SKIP_TEST; 444 return BOOL_FALSE; 445 # endif 446 } 447 448 /* * * * * * * * * * * * * 449 450 run_compiles run all the regexp compiles for all the fixes once. 451 */ 452 void 453 run_compiles (void) 454 { 455 tFixDesc *p_fixd = fixDescList; 456 int fix_ct = FIX_COUNT; 457 regex_t *p_re = XCNEWVEC (regex_t, REGEX_COUNT); 458 459 /* Make sure compile_re does not stumble across invalid data */ 460 461 memset (&incl_quote_re, '\0', sizeof (regex_t)); 462 463 compile_re (incl_quote_pat, &incl_quote_re, 1, 464 "quoted include", "run_compiles"); 465 466 /* Allow machine name tests to be ignored (testing, mainly) */ 467 468 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*'))) 469 pz_machine = (char*)NULL; 470 471 /* FOR every fixup, ... */ 472 do 473 { 474 tTestDesc *p_test = p_fixd->p_test_desc; 475 int test_ct = p_fixd->test_ct; 476 477 /* IF the machine type pointer is not NULL (we are not in test mode) 478 AND this test is for or not done on particular machines 479 THEN ... */ 480 481 if ( (pz_machine != NULL) 482 && (p_fixd->papz_machs != (const char**) NULL) 483 && ! machine_matches (p_fixd) ) 484 continue; 485 486 /* FOR every test for the fixup, ... */ 487 488 while (--test_ct >= 0) 489 { 490 switch (p_test->type) 491 { 492 case TT_EGREP: 493 case TT_NEGREP: 494 p_test->p_test_regex = p_re++; 495 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0, 496 "select test", p_fixd->fix_name); 497 default: break; 498 } 499 p_test++; 500 } 501 } 502 while (p_fixd++, --fix_ct > 0); 503 } 504 505 506 /* * * * * * * * * * * * * 507 508 create_file Create the output modified file. 509 Input: the name of the file to create 510 Returns: a file pointer to the new, open file */ 511 512 #if defined(S_IRUSR) && defined(S_IWUSR) && \ 513 defined(S_IRGRP) && defined(S_IROTH) 514 515 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 516 #else 517 # define S_IRALL 0644 518 #endif 519 520 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \ 521 defined(S_IROTH) && defined(S_IXOTH) 522 523 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) 524 #else 525 # define S_DIRALL 0755 526 #endif 527 528 529 static FILE * 530 create_file (void) 531 { 532 int fd; 533 FILE *pf; 534 char fname[MAXPATHLEN]; 535 536 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len); 537 538 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL); 539 540 /* We may need to create the directories needed... */ 541 if ((fd < 0) && (errno == ENOENT)) 542 { 543 char *pz_dir = strchr (fname + 1, '/'); 544 struct stat stbf; 545 546 while (pz_dir != (char *) NULL) 547 { 548 *pz_dir = NUL; 549 if (stat (fname, &stbf) < 0) 550 { 551 #ifdef _WIN32 552 mkdir (fname); 553 #else 554 mkdir (fname, S_IFDIR | S_DIRALL); 555 #endif 556 } 557 558 *pz_dir = '/'; 559 pz_dir = strchr (pz_dir + 1, '/'); 560 } 561 562 /* Now, lets try the open again... */ 563 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL); 564 } 565 if (fd < 0) 566 { 567 fprintf (stderr, "Error %d (%s) creating %s\n", 568 errno, xstrerror (errno), fname); 569 exit (EXIT_FAILURE); 570 } 571 if (NOT_SILENT) 572 fprintf (stderr, "Fixed: %s\n", pz_curr_file); 573 pf = fdopen (fd, "w"); 574 575 /* 576 * IF pz_machine is NULL, then we are in some sort of test mode. 577 * Do not insert the current directory name. Use a constant string. 578 */ 579 fprintf (pf, z_std_preamble, 580 (pz_machine == NULL) 581 ? "fixinc/tests/inc" 582 : pz_input_dir, 583 pz_curr_file); 584 585 return pf; 586 } 587 588 589 /* * * * * * * * * * * * * 590 591 test_test make sure a shell-style test expression passes. 592 Input: a pointer to the descriptor of the test to run and 593 the name of the file that we might want to fix 594 Result: APPLY_FIX or SKIP_FIX, depending on the result of the 595 shell script we run. */ 596 #ifndef SEPARATE_FIX_PROC 597 static int 598 test_test (tTestDesc* p_test, char* pz_test_file) 599 { 600 tSCC cmd_fmt[] = 601 "file=%s\n\ 602 if ( test %s ) > /dev/null 2>&1\n\ 603 then echo TRUE\n\ 604 else echo FALSE\n\ 605 fi"; 606 607 char *pz_res; 608 int res; 609 610 static char cmd_buf[4096]; 611 612 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text); 613 pz_res = run_shell (cmd_buf); 614 615 switch (*pz_res) { 616 case 'T': 617 res = APPLY_FIX; 618 break; 619 620 case 'F': 621 res = SKIP_FIX; 622 break; 623 624 default: 625 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n", 626 pz_res, cmd_buf ); 627 res = SKIP_FIX; 628 } 629 630 free ((void *) pz_res); 631 return res; 632 } 633 #else 634 /* 635 * IF we are in MS-DOS land, then whatever shell-type test is required 636 * will, by definition, fail 637 */ 638 #define test_test(t,tf) SKIP_FIX 639 #endif 640 641 /* * * * * * * * * * * * * 642 643 egrep_test make sure an egrep expression is found in the file text. 644 Input: a pointer to the descriptor of the test to run and 645 the pointer to the contents of the file under suspicion 646 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise 647 648 The caller may choose to reverse meaning if the sense of the test 649 is inverted. */ 650 651 static int 652 egrep_test (char* pz_data, tTestDesc* p_test) 653 { 654 #ifdef DEBUG 655 if (p_test->p_test_regex == 0) 656 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n", 657 p_test->pz_test_text); 658 #endif 659 if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0) 660 return APPLY_FIX; 661 return SKIP_FIX; 662 } 663 664 665 /* * * * * * * * * * * * * 666 667 quoted_file_exists Make sure that a file exists before we emit 668 the file name. If we emit the name, our invoking shell will try 669 to copy a non-existing file into the destination directory. */ 670 671 static int 672 quoted_file_exists (const char* pz_src_path, 673 const char* pz_file_path, 674 const char* pz_file) 675 { 676 char z[ MAXPATHLEN ]; 677 char* pz; 678 sprintf (z, "%s/%s/", pz_src_path, pz_file_path); 679 pz = z + strlen ( z ); 680 681 for (;;) { 682 char ch = *pz_file++; 683 if (! ISGRAPH( ch )) 684 return 0; 685 if (ch == '"') 686 break; 687 *pz++ = ch; 688 } 689 *pz = '\0'; 690 { 691 struct stat s; 692 if (stat (z, &s) != 0) 693 return 0; 694 return S_ISREG( s.st_mode ); 695 } 696 } 697 698 699 /* * * * * * * * * * * * * 700 * 701 extract_quoted_files 702 703 The syntax, `#include "file.h"' specifies that the compiler is to 704 search the local directory of the current file before the include 705 list. Consequently, if we have modified a header and stored it in 706 another directory, any files that are included by that modified 707 file in that fashion must also be copied into this new directory. 708 This routine finds those flavors of #include and for each one found 709 emits a triple of: 710 711 1. source directory of the original file 712 2. the relative path file name of the #includ-ed file 713 3. the full destination path for this file 714 715 Input: the text of the file, the file name and a pointer to the 716 match list where the match information was stored. 717 Result: internally nothing. The results are written to stdout 718 for interpretation by the invoking shell */ 719 720 721 static void 722 extract_quoted_files (char* pz_data, 723 const char* pz_fixed_file, 724 regmatch_t* p_re_match) 725 { 726 char *pz_dir_end = strrchr (pz_fixed_file, '/'); 727 char *pz_incl_quot = pz_data; 728 729 if (VLEVEL( VERB_APPLIES )) 730 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file); 731 732 /* Set "pz_fixed_file" to point to the containing subdirectory of the source 733 If there is none, then it is in our current directory, ".". */ 734 735 if (pz_dir_end == (char *) NULL) 736 pz_fixed_file = "."; 737 else 738 *pz_dir_end = '\0'; 739 740 for (;;) 741 { 742 pz_incl_quot += p_re_match->rm_so; 743 744 /* Skip forward to the included file name */ 745 while (*pz_incl_quot != '"') 746 pz_incl_quot++; 747 748 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot)) 749 { 750 /* Print the source directory and the subdirectory 751 of the file in question. */ 752 printf ("%s %s/", pz_src_dir, pz_fixed_file); 753 pz_dir_end = pz_incl_quot; 754 755 /* Append to the directory the relative path of the desired file */ 756 while (*pz_incl_quot != '"') 757 putc (*pz_incl_quot++, stdout); 758 759 /* Now print the destination directory appended with the 760 relative path of the desired file */ 761 printf (" %s/%s/", pz_dest_dir, pz_fixed_file); 762 while (*pz_dir_end != '"') 763 putc (*pz_dir_end++, stdout); 764 765 /* End of entry */ 766 putc ('\n', stdout); 767 } 768 769 /* Find the next entry */ 770 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0) 771 break; 772 } 773 } 774 775 776 /* * * * * * * * * * * * * 777 778 Somebody wrote a *_fix subroutine that we must call. 779 */ 780 #ifndef SEPARATE_FIX_PROC 781 static int 782 internal_fix (int read_fd, tFixDesc* p_fixd) 783 { 784 int fd[2]; 785 786 if (pipe( fd ) != 0) 787 { 788 fprintf (stderr, "Error %d on pipe(2) call\n", errno ); 789 exit (EXIT_FAILURE); 790 } 791 792 for (;;) 793 { 794 pid_t childid = fork(); 795 796 switch (childid) 797 { 798 case -1: 799 break; 800 801 case 0: 802 close (fd[0]); 803 goto do_child_task; 804 805 default: 806 /* 807 * Parent process 808 */ 809 close (read_fd); 810 close (fd[1]); 811 return fd[0]; 812 } 813 814 /* 815 * Parent in error 816 */ 817 fprintf (stderr, z_fork_err, errno, xstrerror (errno), 818 p_fixd->fix_name); 819 { 820 static int failCt = 0; 821 if ((errno != EAGAIN) || (++failCt > 10)) 822 exit (EXIT_FAILURE); 823 sleep (1); 824 } 825 } do_child_task:; 826 827 /* 828 * Close our current stdin and stdout 829 */ 830 close (STDIN_FILENO); 831 close (STDOUT_FILENO); 832 UNLOAD_DATA(); 833 834 /* 835 * Make the fd passed in the stdin, and the write end of 836 * the new pipe become the stdout. 837 */ 838 dup2 (fd[1], STDOUT_FILENO); 839 dup2 (read_fd, STDIN_FILENO); 840 841 apply_fix (p_fixd, pz_curr_file); 842 exit (0); 843 } 844 #endif /* !SEPARATE_FIX_PROC */ 845 846 847 #ifdef SEPARATE_FIX_PROC 848 static void 849 fix_with_system (tFixDesc* p_fixd, 850 tCC* pz_fix_file, 851 tCC* pz_file_source, 852 tCC* pz_temp_file) 853 { 854 char* pz_cmd; 855 char* pz_scan; 856 size_t argsize; 857 858 if (p_fixd->fd_flags & FD_SUBROUTINE) 859 { 860 static const char z_applyfix_prog[] = 861 "/../fixincludes/applyfix" EXE_EXT; 862 863 struct stat buf; 864 argsize = 32 865 + strlen (pz_orig_dir) 866 + sizeof (z_applyfix_prog) 867 + strlen (pz_fix_file) 868 + strlen (pz_file_source) 869 + strlen (pz_temp_file); 870 871 /* Allocate something sure to be big enough for our purposes */ 872 pz_cmd = XNEWVEC (char, argsize); 873 strcpy (pz_cmd, pz_orig_dir); 874 pz_scan = pz_cmd + strlen (pz_orig_dir); 875 876 strcpy (pz_scan, z_applyfix_prog); 877 878 /* IF we can't find the "applyfix" executable file at the first guess, 879 try one level higher up */ 880 if (stat (pz_cmd, &buf) == -1) 881 { 882 strcpy (pz_scan, "/.."); 883 strcpy (pz_scan+3, z_applyfix_prog); 884 } 885 886 pz_scan += strlen (pz_scan); 887 888 /* 889 * Now add the fix number and file names that may be needed 890 */ 891 sprintf (pz_scan, " %ld '%s' '%s' '%s'", p_fixd - fixDescList, 892 pz_fix_file, pz_file_source, pz_temp_file); 893 } 894 else /* NOT an "internal" fix: */ 895 { 896 size_t parg_size; 897 #ifdef __MSDOS__ 898 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick: 899 dst is a temporary file anyway, so we know there's no other 900 file by that name; and DOS's system(3) doesn't mind to 901 clobber existing file in redirection. Besides, with DOS 8+3 902 limited file namespace, we can easily lose if dst already has 903 an extension that is 3 or more characters long. 904 905 I do not think the 8+3 issue is relevant because all the files 906 we operate on are named "*.h", making 8+2 adequate. Anyway, 907 the following bizarre use of 'cat' only works on DOS boxes. 908 It causes the file to be dropped into a temporary file for 909 'cat' to read (pipes do not work on DOS). */ 910 tSCC z_cmd_fmt[] = " '%s' | cat > '%s'"; 911 #else 912 /* Don't use positional formatting arguments because some lame-o 913 implementations cannot cope :-(. */ 914 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s"; 915 #endif 916 tCC** ppArgs = p_fixd->patch_args; 917 918 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file ) 919 + strlen( pz_file_source ); 920 parg_size = argsize; 921 922 923 /* 924 * Compute the size of the command line. Add lotsa extra space 925 * because some of the args to sed use lotsa single quotes. 926 * (This requires three extra bytes per quote. Here we allow 927 * for up to 8 single quotes for each argument, including the 928 * command name "sed" itself. Nobody will *ever* need more. :) 929 */ 930 for (;;) 931 { 932 tCC* p_arg = *(ppArgs++); 933 if (p_arg == NULL) 934 break; 935 argsize += 24 + strlen( p_arg ); 936 } 937 938 /* Estimated buffer size we will need. */ 939 pz_scan = pz_cmd = XNEWVEC (char, argsize); 940 /* How much of it do we allot to the program name and its 941 arguments. */ 942 parg_size = argsize - parg_size; 943 944 ppArgs = p_fixd->patch_args; 945 946 /* 947 * Copy the program name, unquoted 948 */ 949 { 950 tCC* pArg = *(ppArgs++); 951 for (;;) 952 { 953 char ch = *(pArg++); 954 if (ch == NUL) 955 break; 956 *(pz_scan++) = ch; 957 } 958 } 959 960 /* 961 * Copy the program arguments, quoted 962 */ 963 for (;;) 964 { 965 tCC* pArg = *(ppArgs++); 966 char* pz_scan_save; 967 if (pArg == NULL) 968 break; 969 *(pz_scan++) = ' '; 970 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg, 971 parg_size - (pz_scan - pz_cmd) ); 972 /* 973 * Make sure we don't overflow the buffer due to sloppy 974 * size estimation. 975 */ 976 while (pz_scan == (char*)NULL) 977 { 978 size_t already_filled = pz_scan_save - pz_cmd; 979 pz_cmd = xrealloc (pz_cmd, argsize += 100); 980 pz_scan_save = pz_scan = pz_cmd + already_filled; 981 parg_size += 100; 982 pz_scan = make_raw_shell_str( pz_scan, pArg, 983 parg_size - (pz_scan - pz_cmd) ); 984 } 985 } 986 987 /* 988 * add the file machinations. 989 */ 990 #ifdef __MSDOS__ 991 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file ); 992 #else 993 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file, 994 pz_temp_file, pz_temp_file, pz_temp_file); 995 #endif 996 } 997 system( pz_cmd ); 998 free( (void*)pz_cmd ); 999 } 1000 1001 /* * * * * * * * * * * * * 1002 1003 This loop should only cycle for 1/2 of one loop. 1004 "chain_open" starts a process that uses "read_fd" as 1005 its stdin and returns the new fd this process will use 1006 for stdout. */ 1007 1008 #else /* is *NOT* SEPARATE_FIX_PROC */ 1009 static int 1010 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file) 1011 { 1012 tCC* pz_cmd_save; 1013 char* pz_cmd; 1014 1015 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0) 1016 return internal_fix (read_fd, p_fixd); 1017 1018 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0) 1019 { 1020 pz_cmd = NULL; 1021 pz_cmd_save = NULL; 1022 } 1023 else 1024 { 1025 tSCC z_cmd_fmt[] = "file='%s'\n%s"; 1026 pz_cmd = XNEWVEC (char, strlen (p_fixd->patch_args[2]) 1027 + sizeof (z_cmd_fmt) + strlen (pz_fix_file)); 1028 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]); 1029 pz_cmd_save = p_fixd->patch_args[2]; 1030 p_fixd->patch_args[2] = pz_cmd; 1031 } 1032 1033 /* Start a fix process, handing off the previous read fd for its 1034 stdin and getting a new fd that reads from the fix process' stdout. 1035 We normally will not loop, but we will up to 10 times if we keep 1036 getting "EAGAIN" errors. 1037 1038 */ 1039 for (;;) 1040 { 1041 static int failCt = 0; 1042 int fd; 1043 1044 fd = chain_open (read_fd, 1045 (tCC **) p_fixd->patch_args, 1046 (process_chain_head == -1) 1047 ? &process_chain_head : (pid_t *) NULL); 1048 1049 if (fd != -1) 1050 { 1051 read_fd = fd; 1052 break; 1053 } 1054 1055 fprintf (stderr, z_fork_err, errno, xstrerror (errno), 1056 p_fixd->fix_name); 1057 1058 if ((errno != EAGAIN) || (++failCt > 10)) 1059 exit (EXIT_FAILURE); 1060 sleep (1); 1061 } 1062 1063 /* IF we allocated a shell script command, 1064 THEN free it and restore the command format to the fix description */ 1065 if (pz_cmd != (char*)NULL) 1066 { 1067 free ((void*)pz_cmd); 1068 p_fixd->patch_args[2] = pz_cmd_save; 1069 } 1070 1071 return read_fd; 1072 } 1073 #endif 1074 1075 1076 /* * * * * * * * * * * * * 1077 1078 Process the potential fixes for a particular include file. 1079 Input: the original text of the file and the file's name 1080 Result: none. A new file may or may not be created. */ 1081 1082 static t_bool 1083 fix_applies (tFixDesc* p_fixd) 1084 { 1085 const char *pz_fname = pz_curr_file; 1086 const char *pz_scan = p_fixd->file_list; 1087 int test_ct; 1088 tTestDesc *p_test; 1089 1090 # ifdef SEPARATE_FIX_PROC 1091 /* 1092 * There is only one fix that uses a shell script as of this writing. 1093 * I hope to nuke it anyway, it does not apply to DOS and it would 1094 * be painful to implement. Therefore, no "shell" fixes for DOS. 1095 */ 1096 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST)) 1097 return BOOL_FALSE; 1098 # else 1099 if (p_fixd->fd_flags & FD_SKIP_TEST) 1100 return BOOL_FALSE; 1101 # endif 1102 1103 /* IF there is a file name restriction, 1104 THEN ensure the current file name matches one in the pattern */ 1105 1106 if (pz_scan != (char *) NULL) 1107 { 1108 size_t name_len; 1109 1110 while ((pz_fname[0] == '.') && (pz_fname[1] == '/')) 1111 pz_fname += 2; 1112 name_len = strlen (pz_fname); 1113 1114 for (;;) 1115 { 1116 pz_scan = strstr (pz_scan + 1, pz_fname); 1117 /* IF we can't match the string at all, 1118 THEN bail */ 1119 if (pz_scan == (char *) NULL) 1120 return BOOL_FALSE; 1121 1122 /* IF the match is surrounded by the '|' markers, 1123 THEN we found a full match -- time to run the tests */ 1124 1125 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|')) 1126 break; 1127 } 1128 } 1129 1130 /* FOR each test, see if it fails. 1131 IF it does fail, then we go on to the next test */ 1132 1133 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct; 1134 test_ct-- > 0; 1135 p_test++) 1136 { 1137 switch (p_test->type) 1138 { 1139 case TT_TEST: 1140 if (test_test (p_test, pz_curr_file) != APPLY_FIX) { 1141 #ifdef DEBUG 1142 if (VLEVEL( VERB_EVERYTHING )) 1143 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name, 1144 pz_fname, p_fixd->test_ct - test_ct); 1145 #endif 1146 return BOOL_FALSE; 1147 } 1148 break; 1149 1150 case TT_EGREP: 1151 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) { 1152 #ifdef DEBUG 1153 if (VLEVEL( VERB_EVERYTHING )) 1154 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name, 1155 pz_fname, p_fixd->test_ct - test_ct); 1156 #endif 1157 return BOOL_FALSE; 1158 } 1159 break; 1160 1161 case TT_NEGREP: 1162 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) { 1163 #ifdef DEBUG 1164 if (VLEVEL( VERB_EVERYTHING )) 1165 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name, 1166 pz_fname, p_fixd->test_ct - test_ct); 1167 #endif 1168 /* Negated sense */ 1169 return BOOL_FALSE; 1170 } 1171 break; 1172 1173 case TT_FUNCTION: 1174 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data) 1175 != APPLY_FIX) { 1176 #ifdef DEBUG 1177 if (VLEVEL( VERB_EVERYTHING )) 1178 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name, 1179 pz_fname, p_fixd->test_ct - test_ct); 1180 #endif 1181 return BOOL_FALSE; 1182 } 1183 break; 1184 } 1185 } 1186 1187 return BOOL_TRUE; 1188 } 1189 1190 1191 /* * * * * * * * * * * * * 1192 1193 Write out a replacement file */ 1194 1195 static void 1196 write_replacement (tFixDesc* p_fixd) 1197 { 1198 const char* pz_text = p_fixd->patch_args[0]; 1199 1200 if ((pz_text == (char*)NULL) || (*pz_text == NUL)) 1201 return; 1202 1203 { 1204 FILE* out_fp = create_file (); 1205 size_t sz = strlen (pz_text); 1206 fwrite (pz_text, sz, 1, out_fp); 1207 if (pz_text[ sz-1 ] != '\n') 1208 fputc ('\n', out_fp); 1209 fclose (out_fp); 1210 } 1211 } 1212 1213 1214 /* * * * * * * * * * * * * 1215 1216 We have work to do. Read back in the output 1217 of the filtering chain. Compare each byte as we read it with 1218 the contents of the original file. As soon as we find any 1219 difference, we will create the output file, write out all 1220 the matched text and then copy any remaining data from the 1221 output of the filter chain. 1222 */ 1223 static void 1224 test_for_changes (int read_fd) 1225 { 1226 FILE *in_fp = fdopen (read_fd, "r"); 1227 FILE *out_fp = (FILE *) NULL; 1228 unsigned char *pz_cmp = (unsigned char*)pz_curr_data; 1229 1230 #ifdef DO_STATS 1231 fixed_ct++; 1232 #endif 1233 for (;;) 1234 { 1235 int ch; 1236 1237 ch = getc (in_fp); 1238 if (ch == EOF) 1239 break; 1240 ch &= 0xFF; /* all bytes are 8 bits */ 1241 1242 /* IF we are emitting the output 1243 THEN emit this character, too. 1244 */ 1245 if (out_fp != (FILE *) NULL) 1246 putc (ch, out_fp); 1247 1248 /* ELSE if this character does not match the original, 1249 THEN now is the time to start the output. 1250 */ 1251 else if (ch != *pz_cmp) 1252 { 1253 out_fp = create_file (); 1254 1255 #ifdef DO_STATS 1256 altered_ct++; 1257 #endif 1258 /* IF there are matched data, write the matched part now. */ 1259 if ((char*)pz_cmp != pz_curr_data) 1260 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data), 1261 1, out_fp); 1262 1263 /* Emit the current unmatching character */ 1264 putc (ch, out_fp); 1265 } 1266 else 1267 /* ELSE the character matches. Advance the compare ptr */ 1268 pz_cmp++; 1269 } 1270 1271 /* IF we created the output file, ... */ 1272 if (out_fp != (FILE *) NULL) 1273 { 1274 regmatch_t match; 1275 1276 /* Close the file and see if we have to worry about 1277 `#include "file.h"' constructs. */ 1278 fclose (out_fp); 1279 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0) 1280 extract_quoted_files (pz_curr_data, pz_curr_file, &match); 1281 } 1282 1283 fclose (in_fp); 1284 close (read_fd); /* probably redundant, but I'm paranoid */ 1285 } 1286 1287 1288 /* * * * * * * * * * * * * 1289 1290 Process the potential fixes for a particular include file. 1291 Input: the original text of the file and the file's name 1292 Result: none. A new file may or may not be created. */ 1293 1294 void 1295 process (void) 1296 { 1297 tFixDesc *p_fixd = fixDescList; 1298 int todo_ct = FIX_COUNT; 1299 int read_fd = -1; 1300 # ifndef SEPARATE_FIX_PROC 1301 int num_children = 0; 1302 # else /* is SEPARATE_FIX_PROC */ 1303 char* pz_file_source = pz_curr_file; 1304 # endif 1305 1306 if (access (pz_curr_file, R_OK) != 0) 1307 { 1308 int erno = errno; 1309 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n", 1310 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN), 1311 erno, xstrerror (erno)); 1312 return; 1313 } 1314 1315 pz_curr_data = load_file (pz_curr_file); 1316 if (pz_curr_data == (char *) NULL) 1317 return; 1318 1319 #ifdef DO_STATS 1320 process_ct++; 1321 #endif 1322 if (VLEVEL( VERB_PROGRESS ) && have_tty) 1323 fprintf (stderr, "%6lu %-50s \r", 1324 (unsigned long) data_map_size, pz_curr_file); 1325 1326 # ifndef SEPARATE_FIX_PROC 1327 process_chain_head = NOPROCESS; 1328 1329 /* For every fix in our fix list, ... */ 1330 for (; todo_ct > 0; p_fixd++, todo_ct--) 1331 { 1332 if (! fix_applies (p_fixd)) 1333 continue; 1334 1335 if (VLEVEL( VERB_APPLIES )) 1336 fprintf (stderr, "Applying %-24s to %s\n", 1337 p_fixd->fix_name, pz_curr_file); 1338 1339 if (p_fixd->fd_flags & FD_REPLACEMENT) 1340 { 1341 write_replacement (p_fixd); 1342 UNLOAD_DATA(); 1343 return; 1344 } 1345 1346 /* IF we do not have a read pointer, 1347 THEN this is the first fix for the current file. 1348 Open the source file. That will be used as stdin for 1349 the first fix. Any subsequent fixes will use the 1350 stdout descriptor of the previous fix for its stdin. */ 1351 1352 if (read_fd == -1) 1353 { 1354 read_fd = open (pz_curr_file, O_RDONLY); 1355 if (read_fd < 0) 1356 { 1357 fprintf (stderr, "Error %d (%s) opening %s\n", errno, 1358 xstrerror (errno), pz_curr_file); 1359 exit (EXIT_FAILURE); 1360 } 1361 1362 /* Ensure we do not get duplicate output */ 1363 1364 fflush (stdout); 1365 } 1366 1367 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file); 1368 num_children++; 1369 } 1370 1371 /* IF we have a read-back file descriptor, 1372 THEN check for changes and write output if changed. */ 1373 1374 if (read_fd >= 0) 1375 { 1376 test_for_changes (read_fd); 1377 #ifdef DO_STATS 1378 apply_ct += num_children; 1379 #endif 1380 /* Wait for child processes created by chain_open() 1381 to avoid leaving zombies. */ 1382 do { 1383 wait ((int *) NULL); 1384 } while (--num_children > 0); 1385 } 1386 1387 # else /* is SEPARATE_FIX_PROC */ 1388 1389 for (; todo_ct > 0; p_fixd++, todo_ct--) 1390 { 1391 if (! fix_applies (p_fixd)) 1392 continue; 1393 1394 if (VLEVEL( VERB_APPLIES )) 1395 fprintf (stderr, "Applying %-24s to %s\n", 1396 p_fixd->fix_name, pz_curr_file); 1397 1398 if (p_fixd->fd_flags & FD_REPLACEMENT) 1399 { 1400 write_replacement (p_fixd); 1401 UNLOAD_DATA(); 1402 return; 1403 } 1404 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file); 1405 pz_file_source = pz_temp_file; 1406 } 1407 1408 read_fd = open (pz_temp_file, O_RDONLY); 1409 if (read_fd < 0) 1410 { 1411 if (errno != ENOENT) 1412 fprintf (stderr, "error %d (%s) opening output (%s) for read\n", 1413 errno, xstrerror (errno), pz_temp_file); 1414 } 1415 else 1416 { 1417 test_for_changes (read_fd); 1418 /* Unlinking a file while it is still open is a Bad Idea on 1419 DOS/Windows. */ 1420 close (read_fd); 1421 unlink (pz_temp_file); 1422 } 1423 1424 # endif 1425 UNLOAD_DATA(); 1426 } 1427