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