OSDN Git Service

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