OSDN Git Service

8bd43dcb748f5e58c32da3e3cbd5c1a1a8d42bbc
[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
855   if (p_fixd->fd_flags & FD_SUBROUTINE)
856     {
857       static const char z_applyfix_prog[] =
858         "/../fixincludes/applyfix" EXE_EXT;
859
860       struct stat buf;
861       argsize = 32
862               + strlen (pz_orig_dir)
863               + sizeof (z_applyfix_prog)
864               + strlen (pz_fix_file)
865               + strlen (pz_file_source)
866               + strlen (pz_temp_file);
867
868       /* Allocate something sure to be big enough for our purposes */
869       pz_cmd = xmalloc (argsize);
870       strcpy (pz_cmd, pz_orig_dir);
871       pz_scan = pz_cmd + strlen (pz_orig_dir);
872
873       strcpy (pz_scan, z_applyfix_prog);
874
875       /* IF we can't find the "applyfix" executable file at the first guess,
876          try one level higher up  */
877       if (stat (pz_cmd, &buf) == -1)
878         {
879           strcpy (pz_scan, "/..");
880           strcpy (pz_scan+3, z_applyfix_prog);
881         }
882
883       pz_scan += strlen (pz_scan);
884
885       /*
886        *  Now add the fix number and file names that may be needed
887        */
888       sprintf (pz_scan, " %ld '%s' '%s' '%s'", p_fixd - fixDescList,
889                pz_fix_file, pz_file_source, pz_temp_file);
890     }
891   else /* NOT an "internal" fix: */
892     {
893       size_t parg_size;
894 #ifdef __MSDOS__
895       /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
896          dst is a temporary file anyway, so we know there's no other
897          file by that name; and DOS's system(3) doesn't mind to
898          clobber existing file in redirection.  Besides, with DOS 8+3
899          limited file namespace, we can easily lose if dst already has
900          an extension that is 3 or more characters long.
901
902          I do not think the 8+3 issue is relevant because all the files
903          we operate on are named "*.h", making 8+2 adequate.  Anyway,
904          the following bizarre use of 'cat' only works on DOS boxes.
905          It causes the file to be dropped into a temporary file for
906          'cat' to read (pipes do not work on DOS).  */
907       tSCC   z_cmd_fmt[] = " '%s' | cat > '%s'";
908 #else
909       /* Don't use positional formatting arguments because some lame-o
910          implementations cannot cope  :-(.  */
911       tSCC   z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
912 #endif
913       tCC**  ppArgs = p_fixd->patch_args;
914
915       argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
916               + strlen( pz_file_source );
917       parg_size = argsize;
918       
919
920       /*
921        *  Compute the size of the command line.  Add lotsa extra space
922        *  because some of the args to sed use lotsa single quotes.
923        *  (This requires three extra bytes per quote.  Here we allow
924        *  for up to 8 single quotes for each argument, including the
925        *  command name "sed" itself.  Nobody will *ever* need more. :)
926        */
927       for (;;)
928         {
929           tCC* p_arg = *(ppArgs++);
930           if (p_arg == NULL)
931             break;
932           argsize += 24 + strlen( p_arg );
933         }
934
935       /* Estimated buffer size we will need.  */
936       pz_scan = pz_cmd = xmalloc (argsize);
937       /* How much of it do we allot to the program name and its
938          arguments.  */
939       parg_size = argsize - parg_size;
940
941       ppArgs = p_fixd->patch_args;
942
943       /*
944        *  Copy the program name, unquoted
945        */
946       {
947         tCC*   pArg = *(ppArgs++);
948         for (;;)
949           {
950             char ch = *(pArg++);
951             if (ch == NUL)
952               break;
953             *(pz_scan++) = ch;
954           }
955       }
956
957       /*
958        *  Copy the program arguments, quoted
959        */
960       for (;;)
961         {
962           tCC*   pArg = *(ppArgs++);
963           char*  pz_scan_save;
964           if (pArg == NULL)
965             break;
966           *(pz_scan++) = ' ';
967           pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
968                                         parg_size - (pz_scan - pz_cmd) );
969           /*
970            *  Make sure we don't overflow the buffer due to sloppy
971            *  size estimation.
972            */
973           while (pz_scan == (char*)NULL)
974             {
975               size_t already_filled = pz_scan_save - pz_cmd;
976               pz_cmd = xrealloc (pz_cmd, argsize += 100);
977               pz_scan_save = pz_scan = pz_cmd + already_filled;
978               parg_size += 100;
979               pz_scan = make_raw_shell_str( pz_scan, pArg,
980                                             parg_size - (pz_scan - pz_cmd) );
981             }
982         }
983
984       /*
985        *  add the file machinations.
986        */
987 #ifdef __MSDOS__
988       sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
989 #else
990       sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
991                pz_temp_file, pz_temp_file, pz_temp_file);
992 #endif
993     }
994   system( pz_cmd );
995   free( (void*)pz_cmd );
996 }
997
998 /* * * * * * * * * * * * *
999
1000     This loop should only cycle for 1/2 of one loop.
1001     "chain_open" starts a process that uses "read_fd" as
1002     its stdin and returns the new fd this process will use
1003     for stdout.  */
1004
1005 #else /* is *NOT* SEPARATE_FIX_PROC */
1006 static int
1007 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
1008 {
1009   tCC* pz_cmd_save;
1010   char* pz_cmd;
1011
1012   if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1013     return internal_fix (read_fd, p_fixd);
1014
1015   if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1016     {
1017       pz_cmd = NULL;
1018       pz_cmd_save = NULL;
1019     }
1020   else
1021     {
1022       tSCC z_cmd_fmt[] = "file='%s'\n%s";
1023       pz_cmd = xmalloc (strlen (p_fixd->patch_args[2])
1024                         + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
1025       sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1026       pz_cmd_save = p_fixd->patch_args[2];
1027       p_fixd->patch_args[2] = pz_cmd;
1028     }
1029
1030   /*  Start a fix process, handing off the  previous read fd for its
1031       stdin and getting a new fd that reads from the fix process' stdout.
1032       We normally will not loop, but we will up to 10 times if we keep
1033       getting "EAGAIN" errors.
1034
1035       */
1036   for (;;)
1037     {
1038       static int failCt = 0;
1039       int fd;
1040
1041       fd = chain_open (read_fd,
1042                        (tCC **) p_fixd->patch_args,
1043                        (process_chain_head == -1)
1044                        ? &process_chain_head : (pid_t *) NULL);
1045
1046       if (fd != -1)
1047         {
1048           read_fd = fd;
1049           break;
1050         }
1051
1052       fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1053                p_fixd->fix_name);
1054
1055       if ((errno != EAGAIN) || (++failCt > 10))
1056         exit (EXIT_FAILURE);
1057       sleep (1);
1058     }
1059
1060   /*  IF we allocated a shell script command,
1061       THEN free it and restore the command format to the fix description */
1062   if (pz_cmd != (char*)NULL)
1063     {
1064       free ((void*)pz_cmd);
1065       p_fixd->patch_args[2] = pz_cmd_save;
1066     }
1067
1068   return read_fd;
1069 }
1070 #endif
1071
1072
1073 /* * * * * * * * * * * * *
1074
1075    Process the potential fixes for a particular include file.
1076    Input:  the original text of the file and the file's name
1077    Result: none.  A new file may or may not be created.  */
1078
1079 static t_bool
1080 fix_applies (tFixDesc* p_fixd)
1081 {
1082   const char *pz_fname = pz_curr_file;
1083   const char *pz_scan = p_fixd->file_list;
1084   int test_ct;
1085   tTestDesc *p_test;
1086
1087 # ifdef SEPARATE_FIX_PROC
1088   /*
1089    *  There is only one fix that uses a shell script as of this writing.
1090    *  I hope to nuke it anyway, it does not apply to DOS and it would
1091    *  be painful to implement.  Therefore, no "shell" fixes for DOS.
1092    */
1093   if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1094     return BOOL_FALSE;
1095 # else
1096   if (p_fixd->fd_flags & FD_SKIP_TEST)
1097     return BOOL_FALSE;
1098 # endif
1099
1100   /*  IF there is a file name restriction,
1101       THEN ensure the current file name matches one in the pattern  */
1102
1103   if (pz_scan != (char *) NULL)
1104     {
1105       size_t name_len;
1106
1107       while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1108         pz_fname += 2;
1109       name_len = strlen (pz_fname);
1110
1111       for (;;)
1112         {
1113           pz_scan = strstr (pz_scan + 1, pz_fname);
1114           /*  IF we can't match the string at all,
1115               THEN bail  */
1116           if (pz_scan == (char *) NULL)
1117             return BOOL_FALSE;
1118
1119           /*  IF the match is surrounded by the '|' markers,
1120               THEN we found a full match -- time to run the tests  */
1121
1122           if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1123             break;
1124         }
1125     }
1126
1127   /*  FOR each test, see if it fails.
1128       IF it does fail, then we go on to the next test */
1129
1130   for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1131        test_ct-- > 0;
1132        p_test++)
1133     {
1134       switch (p_test->type)
1135         {
1136         case TT_TEST:
1137           if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1138 #ifdef DEBUG
1139             if (VLEVEL( VERB_EVERYTHING ))
1140               fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1141                        pz_fname, p_fixd->test_ct - test_ct);
1142 #endif
1143             return BOOL_FALSE;
1144           }
1145           break;
1146
1147         case TT_EGREP:
1148           if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1149 #ifdef DEBUG
1150             if (VLEVEL( VERB_EVERYTHING ))
1151               fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1152                        pz_fname, p_fixd->test_ct - test_ct);
1153 #endif
1154             return BOOL_FALSE;
1155           }
1156           break;
1157
1158         case TT_NEGREP:
1159           if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1160 #ifdef DEBUG
1161             if (VLEVEL( VERB_EVERYTHING ))
1162               fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1163                        pz_fname, p_fixd->test_ct - test_ct);
1164 #endif
1165             /*  Negated sense  */
1166             return BOOL_FALSE;
1167           }
1168           break;
1169
1170         case TT_FUNCTION:
1171           if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1172               != APPLY_FIX) {
1173 #ifdef DEBUG
1174             if (VLEVEL( VERB_EVERYTHING ))
1175               fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1176                        pz_fname, p_fixd->test_ct - test_ct);
1177 #endif
1178             return BOOL_FALSE;
1179           }
1180           break;
1181         }
1182     }
1183
1184   return BOOL_TRUE;
1185 }
1186
1187
1188 /* * * * * * * * * * * * *
1189
1190    Write out a replacement file  */
1191
1192 static void
1193 write_replacement (tFixDesc* p_fixd)
1194 {
1195    const char* pz_text = p_fixd->patch_args[0];
1196
1197    if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1198      return;
1199
1200    {
1201      FILE* out_fp = create_file ();
1202      fputs (pz_text, out_fp);
1203      fclose (out_fp);
1204    }
1205 }
1206
1207
1208 /* * * * * * * * * * * * *
1209
1210     We have work to do.  Read back in the output
1211     of the filtering chain.  Compare each byte as we read it with
1212     the contents of the original file.  As soon as we find any
1213     difference, we will create the output file, write out all
1214     the matched text and then copy any remaining data from the
1215     output of the filter chain.
1216     */
1217 static void
1218 test_for_changes (int read_fd)
1219 {
1220   FILE *in_fp = fdopen (read_fd, "r");
1221   FILE *out_fp = (FILE *) NULL;
1222   unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1223
1224 #ifdef DO_STATS
1225   fixed_ct++;
1226 #endif
1227   for (;;)
1228     {
1229       int ch;
1230
1231       ch = getc (in_fp);
1232       if (ch == EOF)
1233         break;
1234       ch &= 0xFF; /* all bytes are 8 bits */
1235
1236       /*  IF we are emitting the output
1237           THEN emit this character, too.
1238       */
1239       if (out_fp != (FILE *) NULL)
1240         putc (ch, out_fp);
1241
1242       /*  ELSE if this character does not match the original,
1243           THEN now is the time to start the output.
1244       */
1245       else if (ch != *pz_cmp)
1246         {
1247           out_fp = create_file ();
1248
1249 #ifdef DO_STATS
1250           altered_ct++;
1251 #endif
1252           /*  IF there are matched data, write the matched part now. */
1253           if ((char*)pz_cmp != pz_curr_data)
1254             fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
1255                                         1, out_fp);
1256
1257           /*  Emit the current unmatching character */
1258           putc (ch, out_fp);
1259         }
1260       else
1261         /*  ELSE the character matches.  Advance the compare ptr */
1262         pz_cmp++;
1263     }
1264
1265   /*  IF we created the output file, ... */
1266   if (out_fp != (FILE *) NULL)
1267     {
1268       regmatch_t match;
1269
1270       /* Close the file and see if we have to worry about
1271          `#include "file.h"' constructs.  */
1272       fclose (out_fp);
1273       if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1274         extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1275     }
1276
1277   fclose (in_fp);
1278   close (read_fd);  /* probably redundant, but I'm paranoid */
1279 }
1280
1281
1282 /* * * * * * * * * * * * *
1283
1284    Process the potential fixes for a particular include file.
1285    Input:  the original text of the file and the file's name
1286    Result: none.  A new file may or may not be created.  */
1287
1288 void
1289 process (void)
1290 {
1291   tFixDesc *p_fixd = fixDescList;
1292   int todo_ct = FIX_COUNT;
1293   int read_fd = -1;
1294 # ifndef SEPARATE_FIX_PROC
1295   int num_children = 0;
1296 # else /* is SEPARATE_FIX_PROC */
1297   char* pz_file_source = pz_curr_file;
1298 # endif
1299
1300   if (access (pz_curr_file, R_OK) != 0)
1301     {
1302       int erno = errno;
1303       fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1304                pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1305                erno, xstrerror (erno));
1306       return;
1307     }
1308
1309   pz_curr_data = load_file (pz_curr_file);
1310   if (pz_curr_data == (char *) NULL)
1311     return;
1312
1313 #ifdef DO_STATS
1314   process_ct++;
1315 #endif
1316   if (VLEVEL( VERB_PROGRESS ) && have_tty)
1317     fprintf (stderr, "%6lu %-50s   \r",
1318              (unsigned long) data_map_size, pz_curr_file);
1319
1320 # ifndef SEPARATE_FIX_PROC
1321   process_chain_head = NOPROCESS;
1322
1323   /* For every fix in our fix list, ...  */
1324   for (; todo_ct > 0; p_fixd++, todo_ct--)
1325     {
1326       if (! fix_applies (p_fixd))
1327         continue;
1328
1329       if (VLEVEL( VERB_APPLIES ))
1330         fprintf (stderr, "Applying %-24s to %s\n",
1331                  p_fixd->fix_name, pz_curr_file);
1332
1333       if (p_fixd->fd_flags & FD_REPLACEMENT)
1334         {
1335           write_replacement (p_fixd);
1336           UNLOAD_DATA();
1337           return;
1338         }
1339
1340       /*  IF we do not have a read pointer,
1341           THEN this is the first fix for the current file.
1342           Open the source file.  That will be used as stdin for
1343           the first fix.  Any subsequent fixes will use the
1344           stdout descriptor of the previous fix for its stdin.  */
1345
1346       if (read_fd == -1)
1347         {
1348           read_fd = open (pz_curr_file, O_RDONLY);
1349           if (read_fd < 0)
1350             {
1351               fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1352                        xstrerror (errno), pz_curr_file);
1353               exit (EXIT_FAILURE);
1354             }
1355
1356           /*  Ensure we do not get duplicate output */
1357
1358           fflush (stdout);
1359         }
1360
1361       read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1362       num_children++;
1363     }
1364
1365   /*  IF we have a read-back file descriptor,
1366       THEN check for changes and write output if changed.   */
1367
1368   if (read_fd >= 0)
1369     {
1370       test_for_changes (read_fd);
1371 #ifdef DO_STATS
1372       apply_ct += num_children;
1373 #endif
1374       /* Wait for child processes created by chain_open()
1375          to avoid leaving zombies.  */
1376       do  {
1377         wait ((int *) NULL);
1378       } while (--num_children > 0);
1379     }
1380
1381 # else /* is SEPARATE_FIX_PROC */
1382
1383   for (; todo_ct > 0; p_fixd++, todo_ct--)
1384     {
1385       if (! fix_applies (p_fixd))
1386         continue;
1387
1388       if (VLEVEL( VERB_APPLIES ))
1389         fprintf (stderr, "Applying %-24s to %s\n",
1390                  p_fixd->fix_name, pz_curr_file);
1391
1392       if (p_fixd->fd_flags & FD_REPLACEMENT)
1393         {
1394           write_replacement (p_fixd);
1395           UNLOAD_DATA();
1396           return;
1397         }
1398       fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1399       pz_file_source = pz_temp_file;
1400     }
1401
1402   read_fd = open (pz_temp_file, O_RDONLY);
1403   if (read_fd < 0)
1404     {
1405       if (errno != ENOENT)
1406         fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1407                  errno, xstrerror (errno), pz_temp_file);
1408     }
1409   else
1410     {
1411       test_for_changes (read_fd);
1412       /* Unlinking a file while it is still open is a Bad Idea on
1413          DOS/Windows.  */
1414       close (read_fd);
1415       unlink (pz_temp_file);
1416     }
1417
1418 # endif
1419   UNLOAD_DATA();
1420 }