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