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