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