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
main(int argc,char ** argv)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
do_version(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
initialize(int argc,char ** argv)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 *
load_file(const char * fname)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
machine_matches(tFixDesc * p_fixd)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
run_compiles(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 *
create_file(void)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
test_test(tTestDesc * p_test,char * pz_test_file)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
test_test(tTestDesc * p_test,char * pz_test_file)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
egrep_test(char * pz_data,tTestDesc * p_test)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
cksum_test(char * pz_data,tTestDesc * p_test,char * fname)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
quoted_file_exists(const char * pz_src_path,const char * pz_file_path,const char * pz_file)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
extract_quoted_files(char * pz_data,const char * pz_fixed_file,regmatch_t * p_re_match)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
internal_fix(int read_fd,tFixDesc * p_fixd)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
fix_with_system(tFixDesc * p_fixd,tCC * pz_fix_file,tCC * pz_file_source,tCC * pz_temp_file)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
start_fixer(int read_fd,tFixDesc * p_fixd,char * pz_fix_file)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
fix_applies(tFixDesc * p_fixd)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
write_replacement(tFixDesc * p_fixd)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
test_for_changes(int read_fd)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
process(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