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