OSDN Git Service

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