OSDN Git Service

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