OSDN Git Service

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