OSDN Git Service

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