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