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