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 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n"; 78 regex_t incl_quote_re; 79 80 static void do_version (void) ATTRIBUTE_NORETURN; 81 char *load_file (const char *); 82 void run_compiles (void); 83 void initialize (int argc, char** argv); 84 void process (void); 85 86 /* External Source Code */ 87 88 #include "fixincl.x" 89 90 /* * * * * * * * * * * * * * * * * * * 91 * 92 * MAIN ROUTINE 93 */ 94 extern int main (int, char **); 95 int 96 main (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 "\ 158 Processed %5d files containing %d bytes \n\ 159 Applying %5d fixes to %d files\n\ 160 Altering %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 174 static void 175 do_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 197 void 198 initialize ( 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. */ 293 char * 294 load_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 347 static int 348 machine_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 */ 379 void 380 run_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 465 static FILE * 466 create_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 533 static int 534 test_test (tTestDesc* p_test, char* pz_test_file) 535 { 536 tSCC cmd_fmt[] = 537 "file=%s\n\ 538 if ( test %s ) > /dev/null 2>&1\n\ 539 then echo TRUE\n\ 540 else echo FALSE\n\ 541 fi"; 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 587 static int 588 egrep_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 603 quoted_file_exists Make sure that a file exists before we emit 604 the file name. If we emit the name, our invoking shell will try 605 to copy a non-existing file into the destination directory. */ 606 607 static int 608 quoted_file_exists (const char* pz_src_path, 609 const char* pz_file_path, 610 const char* pz_file) 611 { 612 char z[ MAXPATHLEN ]; 613 char* pz; 614 sprintf (z, "%s/%s/", pz_src_path, pz_file_path); 615 pz = z + strlen ( z ); 616 617 for (;;) { 618 char ch = *pz_file++; 619 if (! ISGRAPH( ch )) 620 return 0; 621 if (ch == '"') 622 break; 623 *pz++ = ch; 624 } 625 *pz = '\0'; 626 { 627 struct stat s; 628 if (stat (z, &s) != 0) 629 return 0; 630 return S_ISREG( s.st_mode ); 631 } 632 } 633 634 635 /* * * * * * * * * * * * * 636 * 637 extract_quoted_files 638 639 The syntax, `#include "file.h"' specifies that the compiler is to 640 search the local directory of the current file before the include 641 list. Consequently, if we have modified a header and stored it in 642 another directory, any files that are included by that modified 643 file in that fashion must also be copied into this new directory. 644 This routine finds those flavors of #include and for each one found 645 emits a triple of: 646 647 1. source directory of the original file 648 2. the relative path file name of the #includ-ed file 649 3. the full destination path for this file 650 651 Input: the text of the file, the file name and a pointer to the 652 match list where the match information was stored. 653 Result: internally nothing. The results are written to stdout 654 for interpretation by the invoking shell */ 655 656 657 static void 658 extract_quoted_files (char* pz_data, 659 const char* pz_fixed_file, 660 regmatch_t* p_re_match) 661 { 662 char *pz_dir_end = strrchr (pz_fixed_file, '/'); 663 char *pz_incl_quot = pz_data; 664 665 if (VLEVEL( VERB_APPLIES )) 666 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file); 667 668 /* Set "pz_fixed_file" to point to the containing subdirectory of the source 669 If there is none, then it is in our current directory, ".". */ 670 671 if (pz_dir_end == (char *) NULL) 672 pz_fixed_file = "."; 673 else 674 *pz_dir_end = '\0'; 675 676 for (;;) 677 { 678 pz_incl_quot += p_re_match->rm_so; 679 680 /* Skip forward to the included file name */ 681 while (*pz_incl_quot != '"') 682 pz_incl_quot++; 683 684 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot)) 685 { 686 /* Print the source directory and the subdirectory 687 of the file in question. */ 688 printf ("%s %s/", pz_src_dir, pz_fixed_file); 689 pz_dir_end = pz_incl_quot; 690 691 /* Append to the directory the relative path of the desired file */ 692 while (*pz_incl_quot != '"') 693 putc (*pz_incl_quot++, stdout); 694 695 /* Now print the destination directory appended with the 696 relative path of the desired file */ 697 printf (" %s/%s/", pz_dest_dir, pz_fixed_file); 698 while (*pz_dir_end != '"') 699 putc (*pz_dir_end++, stdout); 700 701 /* End of entry */ 702 putc ('\n', stdout); 703 } 704 705 /* Find the next entry */ 706 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0) 707 break; 708 } 709 } 710 711 712 /* * * * * * * * * * * * * 713 714 Somebody wrote a *_fix subroutine that we must call. 715 */ 716 #ifndef SEPARATE_FIX_PROC 717 static int 718 internal_fix (int read_fd, tFixDesc* p_fixd) 719 { 720 int fd[2]; 721 722 if (pipe( fd ) != 0) 723 { 724 fprintf (stderr, "Error %d on pipe(2) call\n", errno ); 725 exit (EXIT_FAILURE); 726 } 727 728 for (;;) 729 { 730 pid_t childid = fork(); 731 732 switch (childid) 733 { 734 case -1: 735 break; 736 737 case 0: 738 close (fd[0]); 739 goto do_child_task; 740 741 default: 742 /* 743 * Parent process 744 */ 745 close (read_fd); 746 close (fd[1]); 747 return fd[0]; 748 } 749 750 /* 751 * Parent in error 752 */ 753 fprintf (stderr, z_fork_err, errno, xstrerror (errno), 754 p_fixd->fix_name); 755 { 756 static int failCt = 0; 757 if ((errno != EAGAIN) || (++failCt > 10)) 758 exit (EXIT_FAILURE); 759 sleep (1); 760 } 761 } do_child_task:; 762 763 /* 764 * Close our current stdin and stdout 765 */ 766 close (STDIN_FILENO); 767 close (STDOUT_FILENO); 768 UNLOAD_DATA(); 769 770 /* 771 * Make the fd passed in the stdin, and the write end of 772 * the new pipe become the stdout. 773 */ 774 dup2 (fd[1], STDOUT_FILENO); 775 dup2 (read_fd, STDIN_FILENO); 776 777 apply_fix (p_fixd, pz_curr_file); 778 exit (0); 779 } 780 #endif /* !SEPARATE_FIX_PROC */ 781 782 783 #ifdef SEPARATE_FIX_PROC 784 static void 785 fix_with_system (tFixDesc* p_fixd, 786 tCC* pz_fix_file, 787 tCC* pz_file_source, 788 tCC* pz_temp_file) 789 { 790 char* pz_cmd; 791 char* pz_scan; 792 size_t argsize; 793 794 if (p_fixd->fd_flags & FD_SUBROUTINE) 795 { 796 static const char z_applyfix_prog[] = 797 "/../fixincludes/applyfix" EXE_EXT; 798 799 struct stat buf; 800 argsize = 32 801 + strlen (pz_orig_dir) 802 + sizeof (z_applyfix_prog) 803 + strlen (pz_fix_file) 804 + strlen (pz_file_source) 805 + strlen (pz_temp_file); 806 807 /* Allocate something sure to be big enough for our purposes */ 808 pz_cmd = XNEWVEC (char, argsize); 809 strcpy (pz_cmd, pz_orig_dir); 810 pz_scan = pz_cmd + strlen (pz_orig_dir); 811 812 strcpy (pz_scan, z_applyfix_prog); 813 814 /* IF we can't find the "applyfix" executable file at the first guess, 815 try one level higher up */ 816 if (stat (pz_cmd, &buf) == -1) 817 { 818 strcpy (pz_scan, "/.."); 819 strcpy (pz_scan+3, z_applyfix_prog); 820 } 821 822 pz_scan += strlen (pz_scan); 823 824 /* 825 * Now add the fix number and file names that may be needed 826 */ 827 sprintf (pz_scan, " %ld '%s' '%s' '%s'", (long) (p_fixd - fixDescList), 828 pz_fix_file, pz_file_source, pz_temp_file); 829 } 830 else /* NOT an "internal" fix: */ 831 { 832 size_t parg_size; 833 #ifdef __MSDOS__ 834 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick: 835 dst is a temporary file anyway, so we know there's no other 836 file by that name; and DOS's system(3) doesn't mind to 837 clobber existing file in redirection. Besides, with DOS 8+3 838 limited file namespace, we can easily lose if dst already has 839 an extension that is 3 or more characters long. 840 841 I do not think the 8+3 issue is relevant because all the files 842 we operate on are named "*.h", making 8+2 adequate. Anyway, 843 the following bizarre use of 'cat' only works on DOS boxes. 844 It causes the file to be dropped into a temporary file for 845 'cat' to read (pipes do not work on DOS). */ 846 tSCC z_cmd_fmt[] = " '%s' | cat > '%s'"; 847 #else 848 /* Don't use positional formatting arguments because some lame-o 849 implementations cannot cope :-(. */ 850 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s"; 851 #endif 852 tCC** ppArgs = p_fixd->patch_args; 853 854 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file ) 855 + strlen( pz_file_source ); 856 parg_size = argsize; 857 858 859 /* 860 * Compute the size of the command line. Add lotsa extra space 861 * because some of the args to sed use lotsa single quotes. 862 * (This requires three extra bytes per quote. Here we allow 863 * for up to 8 single quotes for each argument, including the 864 * command name "sed" itself. Nobody will *ever* need more. :) 865 */ 866 for (;;) 867 { 868 tCC* p_arg = *(ppArgs++); 869 if (p_arg == NULL) 870 break; 871 argsize += 24 + strlen( p_arg ); 872 } 873 874 /* Estimated buffer size we will need. */ 875 pz_scan = pz_cmd = XNEWVEC (char, argsize); 876 /* How much of it do we allot to the program name and its 877 arguments. */ 878 parg_size = argsize - parg_size; 879 880 ppArgs = p_fixd->patch_args; 881 882 /* 883 * Copy the program name, unquoted 884 */ 885 { 886 tCC* pArg = *(ppArgs++); 887 for (;;) 888 { 889 char ch = *(pArg++); 890 if (ch == NUL) 891 break; 892 *(pz_scan++) = ch; 893 } 894 } 895 896 /* 897 * Copy the program arguments, quoted 898 */ 899 for (;;) 900 { 901 tCC* pArg = *(ppArgs++); 902 char* pz_scan_save; 903 if (pArg == NULL) 904 break; 905 *(pz_scan++) = ' '; 906 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg, 907 parg_size - (pz_scan - pz_cmd) ); 908 /* 909 * Make sure we don't overflow the buffer due to sloppy 910 * size estimation. 911 */ 912 while (pz_scan == (char*)NULL) 913 { 914 size_t already_filled = pz_scan_save - pz_cmd; 915 pz_cmd = xrealloc (pz_cmd, argsize += 100); 916 pz_scan_save = pz_scan = pz_cmd + already_filled; 917 parg_size += 100; 918 pz_scan = make_raw_shell_str( pz_scan, pArg, 919 parg_size - (pz_scan - pz_cmd) ); 920 } 921 } 922 923 /* 924 * add the file machinations. 925 */ 926 #ifdef __MSDOS__ 927 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file ); 928 #else 929 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file, 930 pz_temp_file, pz_temp_file, pz_temp_file); 931 #endif 932 } 933 system( pz_cmd ); 934 free( (void*)pz_cmd ); 935 } 936 937 /* * * * * * * * * * * * * 938 939 This loop should only cycle for 1/2 of one loop. 940 "chain_open" starts a process that uses "read_fd" as 941 its stdin and returns the new fd this process will use 942 for stdout. */ 943 944 #else /* is *NOT* SEPARATE_FIX_PROC */ 945 static int 946 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file) 947 { 948 tCC* pz_cmd_save; 949 char* pz_cmd; 950 951 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0) 952 return internal_fix (read_fd, p_fixd); 953 954 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0) 955 { 956 pz_cmd = NULL; 957 pz_cmd_save = NULL; 958 } 959 else 960 { 961 tSCC z_cmd_fmt[] = "file='%s'\n%s"; 962 pz_cmd = XNEWVEC (char, strlen (p_fixd->patch_args[2]) 963 + sizeof (z_cmd_fmt) + strlen (pz_fix_file)); 964 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]); 965 pz_cmd_save = p_fixd->patch_args[2]; 966 p_fixd->patch_args[2] = pz_cmd; 967 } 968 969 /* Start a fix process, handing off the previous read fd for its 970 stdin and getting a new fd that reads from the fix process' stdout. 971 We normally will not loop, but we will up to 10 times if we keep 972 getting "EAGAIN" errors. 973 974 */ 975 for (;;) 976 { 977 static int failCt = 0; 978 int fd; 979 980 fd = chain_open (read_fd, 981 (tCC **) p_fixd->patch_args, 982 (process_chain_head == -1) 983 ? &process_chain_head : (pid_t *) NULL); 984 985 if (fd != -1) 986 { 987 read_fd = fd; 988 break; 989 } 990 991 fprintf (stderr, z_fork_err, errno, xstrerror (errno), 992 p_fixd->fix_name); 993 994 if ((errno != EAGAIN) || (++failCt > 10)) 995 exit (EXIT_FAILURE); 996 sleep (1); 997 } 998 999 /* IF we allocated a shell script command, 1000 THEN free it and restore the command format to the fix description */ 1001 if (pz_cmd != (char*)NULL) 1002 { 1003 free ((void*)pz_cmd); 1004 p_fixd->patch_args[2] = pz_cmd_save; 1005 } 1006 1007 return read_fd; 1008 } 1009 #endif 1010 1011 1012 /* * * * * * * * * * * * * 1013 * 1014 * Process the potential fixes for a particular include file. 1015 * Input: the original text of the file and the file's name 1016 * Result: none. A new file may or may not be created. 1017 */ 1018 static t_bool 1019 fix_applies (tFixDesc* p_fixd) 1020 { 1021 const char *pz_fname = pz_curr_file; 1022 const char *pz_scan = p_fixd->file_list; 1023 int test_ct; 1024 tTestDesc *p_test; 1025 1026 #ifdef SEPARATE_FIX_PROC 1027 /* 1028 * There is only one fix that uses a shell script as of this writing. 1029 * I hope to nuke it anyway, it does not apply to DOS and it would 1030 * be painful to implement. Therefore, no "shell" fixes for DOS. 1031 */ 1032 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST)) 1033 return BOOL_FALSE; 1034 #else 1035 if (p_fixd->fd_flags & FD_SKIP_TEST) 1036 return BOOL_FALSE; 1037 #endif 1038 1039 /* IF there is a file name restriction, 1040 THEN ensure the current file name matches one in the pattern */ 1041 1042 if (pz_scan != (char *) NULL) 1043 { 1044 while ((pz_fname[0] == '.') && (pz_fname[1] == '/')) 1045 pz_fname += 2; 1046 1047 for (;;) 1048 { 1049 if (fnmatch (pz_scan, pz_fname, 0) == 0) 1050 break; 1051 pz_scan += strlen (pz_scan) + 1; 1052 if (*pz_scan == NUL) 1053 return BOOL_FALSE; 1054 } 1055 } 1056 1057 /* FOR each test, see if it fails. 1058 IF it does fail, then we go on to the next test */ 1059 1060 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct; 1061 test_ct-- > 0; 1062 p_test++) 1063 { 1064 switch (p_test->type) 1065 { 1066 case TT_TEST: 1067 if (test_test (p_test, pz_curr_file) != APPLY_FIX) { 1068 #ifdef DEBUG 1069 if (VLEVEL( VERB_EVERYTHING )) 1070 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name, 1071 pz_fname, p_fixd->test_ct - test_ct); 1072 #endif 1073 return BOOL_FALSE; 1074 } 1075 break; 1076 1077 case TT_EGREP: 1078 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) { 1079 #ifdef DEBUG 1080 if (VLEVEL( VERB_EVERYTHING )) 1081 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name, 1082 pz_fname, p_fixd->test_ct - test_ct); 1083 #endif 1084 return BOOL_FALSE; 1085 } 1086 break; 1087 1088 case TT_NEGREP: 1089 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) { 1090 #ifdef DEBUG 1091 if (VLEVEL( VERB_EVERYTHING )) 1092 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name, 1093 pz_fname, p_fixd->test_ct - test_ct); 1094 #endif 1095 /* Negated sense */ 1096 return BOOL_FALSE; 1097 } 1098 break; 1099 1100 case TT_FUNCTION: 1101 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data) 1102 != APPLY_FIX) { 1103 #ifdef DEBUG 1104 if (VLEVEL( VERB_EVERYTHING )) 1105 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name, 1106 pz_fname, p_fixd->test_ct - test_ct); 1107 #endif 1108 return BOOL_FALSE; 1109 } 1110 break; 1111 } 1112 } 1113 1114 return BOOL_TRUE; 1115 } 1116 1117 1118 /* * * * * * * * * * * * * 1119 1120 Write out a replacement file */ 1121 1122 static void 1123 write_replacement (tFixDesc* p_fixd) 1124 { 1125 const char* pz_text = p_fixd->patch_args[0]; 1126 1127 if ((pz_text == (char*)NULL) || (*pz_text == NUL)) 1128 return; 1129 1130 { 1131 FILE* out_fp = create_file (); 1132 size_t sz = strlen (pz_text); 1133 fwrite (pz_text, sz, 1, out_fp); 1134 if (pz_text[ sz-1 ] != '\n') 1135 fputc ('\n', out_fp); 1136 fclose (out_fp); 1137 } 1138 } 1139 1140 1141 /* * * * * * * * * * * * * 1142 1143 We have work to do. Read back in the output 1144 of the filtering chain. Compare each byte as we read it with 1145 the contents of the original file. As soon as we find any 1146 difference, we will create the output file, write out all 1147 the matched text and then copy any remaining data from the 1148 output of the filter chain. 1149 */ 1150 static void 1151 test_for_changes (int read_fd) 1152 { 1153 FILE *in_fp = fdopen (read_fd, "r"); 1154 FILE *out_fp = (FILE *) NULL; 1155 unsigned char *pz_cmp = (unsigned char*)pz_curr_data; 1156 1157 #ifdef DO_STATS 1158 fixed_ct++; 1159 #endif 1160 for (;;) 1161 { 1162 int ch; 1163 1164 ch = getc (in_fp); 1165 if (ch == EOF) 1166 break; 1167 ch &= 0xFF; /* all bytes are 8 bits */ 1168 1169 /* IF we are emitting the output 1170 THEN emit this character, too. 1171 */ 1172 if (out_fp != (FILE *) NULL) 1173 putc (ch, out_fp); 1174 1175 /* ELSE if this character does not match the original, 1176 THEN now is the time to start the output. 1177 */ 1178 else if (ch != *pz_cmp) 1179 { 1180 out_fp = create_file (); 1181 1182 #ifdef DO_STATS 1183 altered_ct++; 1184 #endif 1185 /* IF there are matched data, write the matched part now. */ 1186 if ((char*)pz_cmp != pz_curr_data) 1187 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data), 1188 1, out_fp); 1189 1190 /* Emit the current unmatching character */ 1191 putc (ch, out_fp); 1192 } 1193 else 1194 /* ELSE the character matches. Advance the compare ptr */ 1195 pz_cmp++; 1196 } 1197 1198 /* IF we created the output file, ... */ 1199 if (out_fp != (FILE *) NULL) 1200 { 1201 regmatch_t match; 1202 1203 /* Close the file and see if we have to worry about 1204 `#include "file.h"' constructs. */ 1205 fclose (out_fp); 1206 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0) 1207 extract_quoted_files (pz_curr_data, pz_curr_file, &match); 1208 } 1209 1210 fclose (in_fp); 1211 close (read_fd); /* probably redundant, but I'm paranoid */ 1212 } 1213 1214 1215 /* * * * * * * * * * * * * 1216 1217 Process the potential fixes for a particular include file. 1218 Input: the original text of the file and the file's name 1219 Result: none. A new file may or may not be created. */ 1220 1221 void 1222 process (void) 1223 { 1224 tFixDesc *p_fixd = fixDescList; 1225 int todo_ct = FIX_COUNT; 1226 int read_fd = -1; 1227 # ifndef SEPARATE_FIX_PROC 1228 int num_children = 0; 1229 # else /* is SEPARATE_FIX_PROC */ 1230 char* pz_file_source = pz_curr_file; 1231 # endif 1232 1233 if (access (pz_curr_file, R_OK) != 0) 1234 { 1235 int erno = errno; 1236 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n", 1237 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN), 1238 erno, xstrerror (erno)); 1239 return; 1240 } 1241 1242 pz_curr_data = load_file (pz_curr_file); 1243 if (pz_curr_data == (char *) NULL) 1244 return; 1245 1246 #ifdef DO_STATS 1247 process_ct++; 1248 #endif 1249 if (VLEVEL( VERB_PROGRESS ) && have_tty) 1250 fprintf (stderr, "%6lu %-50s \r", 1251 (unsigned long) data_map_size, pz_curr_file); 1252 1253 # ifndef SEPARATE_FIX_PROC 1254 process_chain_head = NOPROCESS; 1255 1256 /* For every fix in our fix list, ... */ 1257 for (; todo_ct > 0; p_fixd++, todo_ct--) 1258 { 1259 if (! fix_applies (p_fixd)) 1260 continue; 1261 1262 if (VLEVEL( VERB_APPLIES )) 1263 fprintf (stderr, "Applying %-24s to %s\n", 1264 p_fixd->fix_name, pz_curr_file); 1265 1266 if (p_fixd->fd_flags & FD_REPLACEMENT) 1267 { 1268 write_replacement (p_fixd); 1269 UNLOAD_DATA(); 1270 return; 1271 } 1272 1273 /* IF we do not have a read pointer, 1274 THEN this is the first fix for the current file. 1275 Open the source file. That will be used as stdin for 1276 the first fix. Any subsequent fixes will use the 1277 stdout descriptor of the previous fix for its stdin. */ 1278 1279 if (read_fd == -1) 1280 { 1281 read_fd = open (pz_curr_file, O_RDONLY); 1282 if (read_fd < 0) 1283 { 1284 fprintf (stderr, "Error %d (%s) opening %s\n", errno, 1285 xstrerror (errno), pz_curr_file); 1286 exit (EXIT_FAILURE); 1287 } 1288 1289 /* Ensure we do not get duplicate output */ 1290 1291 fflush (stdout); 1292 } 1293 1294 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file); 1295 num_children++; 1296 } 1297 1298 /* IF we have a read-back file descriptor, 1299 THEN check for changes and write output if changed. */ 1300 1301 if (read_fd >= 0) 1302 { 1303 test_for_changes (read_fd); 1304 #ifdef DO_STATS 1305 apply_ct += num_children; 1306 #endif 1307 /* Wait for child processes created by chain_open() 1308 to avoid leaving zombies. */ 1309 do { 1310 wait ((int *) NULL); 1311 } while (--num_children > 0); 1312 } 1313 1314 # else /* is SEPARATE_FIX_PROC */ 1315 1316 for (; todo_ct > 0; p_fixd++, todo_ct--) 1317 { 1318 if (! fix_applies (p_fixd)) 1319 continue; 1320 1321 if (VLEVEL( VERB_APPLIES )) 1322 fprintf (stderr, "Applying %-24s to %s\n", 1323 p_fixd->fix_name, pz_curr_file); 1324 1325 if (p_fixd->fd_flags & FD_REPLACEMENT) 1326 { 1327 write_replacement (p_fixd); 1328 UNLOAD_DATA(); 1329 return; 1330 } 1331 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file); 1332 pz_file_source = pz_temp_file; 1333 } 1334 1335 read_fd = open (pz_temp_file, O_RDONLY); 1336 if (read_fd < 0) 1337 { 1338 if (errno != ENOENT) 1339 fprintf (stderr, "error %d (%s) opening output (%s) for read\n", 1340 errno, xstrerror (errno), pz_temp_file); 1341 } 1342 else 1343 { 1344 test_for_changes (read_fd); 1345 /* Unlinking a file while it is still open is a Bad Idea on 1346 DOS/Windows. */ 1347 close (read_fd); 1348 unlink (pz_temp_file); 1349 } 1350 1351 # endif 1352 UNLOAD_DATA(); 1353 } 1354