OSDN Git Service

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