OSDN Git Service

A fix for one breaks another :( - do not ignore SIGCLD!
[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 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 "auto-host.h"
26
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #ifdef HAVE_SYS_WAIT_H
31 #include <sys/wait.h>
32 #endif
33 #include <signal.h>
34 #include <stdio.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <string.h>
41 #ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 #endif
44 #include <ctype.h>
45
46 #include "gnu-regex.h"
47 #include "server.h"
48
49 static const char program_id[] = "fixincl version 1.0";
50
51 #define MINIMUM_MAXIMUM_LINES   128
52
53 /* If this particular system's header files define the macro `MAXPATHLEN',
54    we happily take advantage of it; otherwise we use a value which ought
55    to be large enough.  */
56 #ifndef MAXPATHLEN
57 # define MAXPATHLEN     4096
58 #endif
59 #define NAME_TABLE_SIZE (MINIMUM_MAXIMUM_LINES * MAXPATHLEN)
60
61 #ifndef EXIT_SUCCESS
62 # define EXIT_SUCCESS 0
63 #endif
64 #ifndef EXIT_FAILURE
65 # define EXIT_FAILURE 1
66 #endif
67
68 char *file_name_buf;
69
70 #define tSCC static const char
71 #define tCC  const char
72 #define tSC  static char
73
74 typedef int t_success;
75
76 #define FAILURE         (-1)
77 #define SUCCESS           0
78 #define PROBLEM           1
79
80 #define SUCCEEDED(p)    ((p) == SUCCESS)
81 #define SUCCESSFUL(p)   SUCCEEDED (p)
82 #define FAILED(p)       ((p) < SUCCESS)
83 #define HADGLITCH(p)    ((p) > SUCCESS)
84
85 #define NUL             '\0'
86
87 /*  Test Descriptor
88
89     Each fix may have associated tests that determine
90     whether the fix needs to be applied or not.
91     Each test has a type (from the te_test_type enumeration);
92     associated test text; and, if the test is TT_EGREP or
93     the negated form TT_NEGREP, a pointer to the compiled
94     version of the text string.
95
96     */
97 typedef enum
98 {
99   TT_TEST, TT_EGREP, TT_NEGREP
100 } te_test_type;
101
102 typedef struct test_desc tTestDesc;
103
104 struct test_desc
105 {
106   te_test_type type;
107   const char *pz_test_text;
108   regex_t *p_test_regex;
109 };
110
111 typedef struct patch_desc tPatchDesc;
112
113 /*  Fix Descriptor
114
115     Everything you ever wanted to know about how to apply
116     a particular fix (which files, how to qualify them,
117     how to actually make the fix, etc...)
118
119     */
120 #define FD_MACH_ONLY      0x0000
121 #define FD_MACH_IFNOT     0x0001
122 #define FD_SKIP_TEST      0x8000
123
124 typedef struct fix_desc tFixDesc;
125 struct fix_desc
126 {
127   const char*   fix_name;       /* Name of the fix */
128   const char*   file_list;      /* List of files it applies to */
129   const char**  papz_machs;     /* List of machine/os-es it applies to */
130   regex_t*      unused;
131   int           test_ct;
132   int           fd_flags;
133   tTestDesc*    p_test_desc;
134   const char**  patch_args;
135 };
136
137 /*  Working environment strings.  Essentially, invocation 'options'.  */
138 char *pz_dest_dir = NULL;
139 char *pz_src_dir = NULL;
140 char *pz_machine = NULL;
141 char *pz_find_base = NULL;
142 int find_base_len = 0;
143
144 pid_t process_chain_head = (pid_t) -1;
145
146 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
147 regex_t incl_quote_re;
148
149 char *load_file  _P_((const char *));
150 void process  _P_((char *, const char *));
151 void run_compiles ();
152 void wait_for_pid _P_(( pid_t ));
153 void initialize ();
154
155 #include "fixincl.x"
156
157 /* * * * * * * * * * * * * * * * * * *
158  *
159  *  MAIN ROUTINE
160  */
161 int
162 main (argc, argv)
163      int argc;
164      char **argv;
165 {
166   static const char gnu_lib_mark[] =
167     "This file is part of the GNU C Library";
168
169 #ifndef NO_BOGOSITY_LIMITS
170 # define BOGUS_LIMIT    MINIMUM_MAXIMUM_LINES
171   size_t loop_ct;
172 #endif
173
174   char *apz_names[BOGUS_LIMIT];
175   size_t file_name_ct;
176
177   /* Before anything else, ensure we can allocate our file name buffer. */
178   file_name_buf = (char *) malloc (NAME_TABLE_SIZE);
179   if (file_name_buf == (char *) NULL)
180     {
181       fprintf (stderr, "fixincl cannot allocate 0x%08X bytes\n",
182                NAME_TABLE_SIZE);
183       exit (EXIT_FAILURE);
184     }
185
186   switch (argc)
187     {
188     case 1:
189       break;
190
191     case 2:
192       if (strcmp (argv[1], "-v") == 0)
193         {
194           static const char zFmt[] = "echo '%s'";
195
196           /* The 'version' option is really used to test that:
197                1.  The program loads correctly (no missing libraries)
198                2.  we can correctly run our server shell process
199                3.  that we can compile all the regular expressions.
200            */
201           run_compiles ();
202           sprintf (file_name_buf, zFmt, program_id);
203           fputs (file_name_buf + 5, stdout);
204           exit (strcmp (run_shell (file_name_buf), program_id));
205         }
206       freopen (argv[1], "r", stdin);
207       break;
208
209     default:
210       fputs ("fixincl ERROR:  too many command line arguments\n", stderr);
211       exit (EXIT_FAILURE);
212     }
213
214   initialize ();
215
216 #ifndef NO_BOGOSITY_LIMITS
217   /*  Some systems only allow so many calls to fork(2).
218       This is inadequate for this program.  Consequently,
219       we must let a grandfather process spawn children
220       that then spawn all the processes that do the real work.
221       */
222   for (;;)
223     {
224       file_name_ct = 0;
225
226       {
227         char *pz_buf = file_name_buf;
228
229         /* Only the parent process can read from stdin without confusing
230            the world. (How does the child tell the parent to skip
231            forward?  Pipes and files behave differently.)  */
232
233         while (  (file_name_ct < BOGUS_LIMIT)
234               && (pz_buf < (file_name_buf + NAME_TABLE_SIZE - MAXPATHLEN)))
235           {
236             if (fgets (pz_buf, MAXPATHLEN, stdin) == (char *) NULL)
237               break;
238             while (isspace (*pz_buf))
239               pz_buf++;
240             if ((*pz_buf == '\0') || (*pz_buf == '#'))
241               continue;
242             apz_names[file_name_ct++] = pz_buf;
243             pz_buf += strlen (pz_buf);
244             while (isspace (pz_buf[-1]))
245               pz_buf--;
246             *pz_buf++ = '\0';
247           }
248       }
249
250       /*  IF we did not get any files this time thru
251           THEN we must be done.  */
252       if (file_name_ct == 0)
253         return EXIT_SUCCESS;
254
255       fflush (stdout);
256       fflush (stderr);
257
258       {
259         pid_t child = fork ();
260         if (child == NULLPROCESS)
261           break;
262
263         if (child == NOPROCESS)
264           {
265             fprintf (stderr, "Error %d (%s) forking in main\n",
266                      errno, strerror (errno));
267             exit (EXIT_FAILURE);
268           }
269
270 #ifdef DEBUG
271         fprintf (stderr, "Waiting for %d to complete %d files\n",
272                  child, file_name_ct);
273 #endif
274
275         wait_for_pid( child, file_name_ct );
276       }
277     }
278 #else
279  /*#*/ error "NON-BOGUS LIMITS NOT SUPPORTED?!?!"
280 #endif
281
282   /*  For every file specified in stdandard in
283       (except as throttled for bogus reasons)...
284       */
285   for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++)
286     {
287       char *pz_data;
288       char *pz_file_name = apz_names[loop_ct];
289
290       if (access (pz_file_name, R_OK) != 0)
291         {
292           int erno = errno;
293           fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
294                    pz_file_name, getcwd ((char *) NULL, MAXPATHLEN),
295                    erno, strerror (erno));
296         }
297       else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL))
298         {
299           if (strstr (pz_data, gnu_lib_mark) == (char *) NULL)
300             process (pz_data, pz_file_name);
301           free ((void *) pz_data);
302         }
303     }
304
305   return EXIT_SUCCESS;
306 }
307
308
309 /* * * * * * * * * * * * */
310
311 void
312 initialize()
313 {
314   static const char var_not_found[] =
315     "fixincl ERROR:  %s environment variable not defined\n";
316
317   {
318     static const char var[] = "TARGET_MACHINE";
319     pz_machine = getenv (var);
320     if (pz_machine == (char *) NULL)
321       {
322         fprintf (stderr, var_not_found, var);
323         exit (EXIT_FAILURE);
324       }
325   }
326
327   {
328     static const char var[] = "DESTDIR";
329     pz_dest_dir = getenv (var);
330     if (pz_dest_dir == (char *) NULL)
331       {
332         fprintf (stderr, var_not_found, var);
333         exit (EXIT_FAILURE);
334       }
335   }
336
337   {
338     static const char var[] = "SRCDIR";
339     pz_src_dir = getenv (var);
340     if (pz_src_dir == (char *) NULL)
341       {
342         fprintf (stderr, var_not_found, var);
343         exit (EXIT_FAILURE);
344       }
345   }
346
347   {
348     static const char var[] = "FIND_BASE";
349     pz_find_base = getenv (var);
350     if (pz_find_base == (char *) NULL)
351       {
352         fprintf (stderr, var_not_found, var);
353         exit (EXIT_FAILURE);
354       }
355     find_base_len = strlen( pz_find_base );
356   }
357
358   /*  Compile all the regular expressions now.
359       That way, it is done only once for the whole run.
360       */
361   run_compiles ();
362
363   signal (SIGQUIT, SIG_IGN);
364   signal (SIGIOT,  SIG_IGN);
365   signal (SIGPIPE, SIG_IGN);
366   signal (SIGALRM, SIG_IGN);
367   signal (SIGTERM, SIG_IGN);
368
369   /*
370      Make sure that if we opened a server process, we close it now.
371      This is the grandparent process.  We don't need the server anymore
372      and our children should make their own.  */
373
374   close_server ();
375   (void)wait ( (int*)NULL );
376 }
377
378 /* * * * * * * * * * * * *
379  
380    wait_for_pid  -  Keep calling `wait(2)' until it returns
381    the process id we are looking for.  Not every system has
382    `waitpid(2)'.  We also ensure that the children exit with success. */
383
384 void
385 wait_for_pid(child, file_name_ct)
386      pid_t child;
387      int file_name_ct;
388 {
389   for (;;) {
390     int status;
391     pid_t dead_kid = wait (&status);
392
393     if (dead_kid == child)
394       {
395         if (! WIFEXITED( status ))
396           {
397             fprintf (stderr, "child process %d is hung on signal %d\n",
398                      child, WSTOPSIG( status ));
399             exit (EXIT_FAILURE);
400           }
401         if (WEXITSTATUS( status ) != 0)
402           {
403             fprintf (stderr, "child process %d exited with status %d\n",
404                      child, WEXITSTATUS( status ));
405             exit (EXIT_FAILURE);
406           }
407         break; /* normal child completion */
408       }
409
410     /*
411        IF there is an error, THEN see if it is retryable.
412        If it is not retryable, then break out of this loop.  */
413     if (dead_kid == NOPROCESS)
414       {
415         switch (errno) {
416         case EINTR:
417         case EAGAIN:
418           break;
419
420         default:
421           fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
422                    errno, strerror( errno ), child );
423           /* FALLTHROUGH */
424
425         case ECHILD: /* no children to wait for?? */
426           return;
427         }
428       }
429   } done_waiting:;
430 }
431
432
433 /* * * * * * * * * * * * *
434  
435    load_file loads all the contents of a file into malloc-ed memory.
436    Its argument is the name of the file to read in; the returned
437    result is the NUL terminated contents of the file.  The file
438    is presumed to be an ASCII text file containing no NULs.  */
439 char *
440 load_file (pz_file_name)
441      const char *pz_file_name;
442 {
443   char *pz_data;
444   size_t file_size;
445
446   {
447     struct stat stbf;
448     
449     if (stat (pz_file_name, &stbf) != 0)
450       {
451         fprintf (stderr, "error %d (%s) stat-ing %s\n",
452                  errno, strerror (errno), pz_file_name);
453         return (char *) NULL;
454       }
455     file_size = stbf.st_size;
456   }
457   if (file_size == 0)
458     return (char *) NULL;
459
460   pz_data = (char *) malloc ((file_size + 16) & ~0x00F);
461   if (pz_data == (char *) NULL)
462     {
463       fprintf (stderr, "error:  could not malloc %d bytes\n",
464                file_size);
465       exit (EXIT_FAILURE);
466     }
467
468   {
469     FILE *fp = fopen (pz_file_name, "r");
470     size_t size_left = file_size;
471     char *read_ptr = pz_data;
472
473     if (fp == (FILE *) NULL)
474       {
475         fprintf (stderr, "error %d (%s) opening %s\n", errno,
476                  strerror (errno), pz_file_name);
477         free ((void *) pz_data);
478         return (char *) NULL;
479       }
480
481     do
482       {
483         size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp);
484
485         if (sizeRead == 0)
486           {
487             if (feof (fp))
488               break;
489
490             if (ferror (fp))
491               {
492                 int err = errno;
493                 if (err != EISDIR)
494                   fprintf (stderr, "error %d (%s) reading %s\n", err,
495                            strerror (err), pz_file_name);
496                 free ((void *) pz_data);
497                 fclose (fp);
498                 return (char *) NULL;
499               }
500           }
501
502         read_ptr += sizeRead;
503         size_left -= sizeRead;
504       }
505     while (size_left != 0);
506
507     *read_ptr = '\0';
508     fclose (fp);
509   }
510   return pz_data;
511 }
512
513
514 /* * * * * * * * * * * * *
515  
516    run_compiles   run all the regexp compiles for all the fixes once.
517  */
518 void
519 run_compiles ()
520 {
521   tSCC z_bad_comp[] = "fixincl ERROR:  cannot compile %s regex for %s\n\
522 \texpr = `%s'\n\terror %s\n";
523   tFixDesc *p_fixd = fixDescList;
524   int fix_ct = FIX_COUNT;
525   tTestDesc *p_test;
526   int test_ct;
527   int re_ct = REGEX_COUNT;
528   const char *pz_err;
529   regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
530
531   if (p_re == (regex_t *) NULL)
532     {
533       fprintf (stderr, "fixincl ERROR:  cannot allocate %d bytes for regex\n",
534                REGEX_COUNT * sizeof (regex_t));
535       exit (EXIT_FAILURE);
536     }
537
538   /*  Make sure re_compile_pattern does not stumble across invalid
539       data */
540
541   memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
542   memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
543
544   /*  The patterns we search for are all egrep patterns.
545       In the shell version of this program, we invoke egrep
546       with the supplied pattern.  Here, we will run
547       re_compile_pattern, but it must be using the same rules.  */
548
549   re_set_syntax (RE_SYNTAX_EGREP);
550   pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
551                               &incl_quote_re);
552   if (pz_err != (char *) NULL)
553     {
554       fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
555                incl_quote_pat, pz_err);
556       exit (EXIT_FAILURE);
557     }
558
559   /* FOR every fixup, ...  */
560   do
561     {
562       p_test = p_fixd->p_test_desc;
563       test_ct = p_fixd->test_ct;
564
565       /*  IF the machine type pointer is not NULL (we are not in test mode)
566              AND this test is for or not done on particular machines
567           THEN ...   */
568
569       if (  (pz_machine != NULL)
570          && (p_fixd->papz_machs != (const char**) NULL) )
571         {
572           const char **papz_machs = p_fixd->papz_machs;
573           char *pz = file_name_buf;
574           char *pz_sep = "";
575           tCC *pz_if_true;
576           tCC *pz_if_false;
577           tSCC skip[] = "skip";
578           tSCC run[] = "run";
579
580           /*  Construct a shell script that looks like this:
581
582               case our-cpu-platform-os in
583               tests-cpu-platform-os-pattern )
584                   echo run ;;
585               * )
586                   echo skip ;;
587               esac
588
589               where 'run' and 'skip' may be reversed, depending on
590               the sense of the test.  */
591
592           sprintf (pz, "case %s in\n", pz_machine);
593           pz += strlen (pz);
594
595           if (p_fixd->fd_flags & FD_MACH_IFNOT)
596             {
597               pz_if_true  = skip;
598               pz_if_false = run;
599             }
600           else
601             {
602               pz_if_true  = run;
603               pz_if_false = skip;
604             }
605
606           /*  FOR any additional machine names to test for,
607               insert the " | \\\n" glue and the machine pattern.  */
608
609           for (;;)
610             {
611               const char* pz_mach = *(papz_machs++);
612
613               if (pz_mach == (const char*) NULL)
614                 break;
615               sprintf (pz, "%s  %s", pz_sep, pz_mach);
616               pz += strlen (pz);
617               pz_sep = " | \\\n";
618             }
619           sprintf (pz, " )\n    echo %s ;;\n  * )\n    echo %s ;;\nesac",
620                    pz_if_true, pz_if_false);
621
622           /*  Run the script.
623               The result will start either with 's' or 'r'.  */
624
625           {
626             int skip;
627             pz = run_shell (file_name_buf);
628             skip = (*pz == 's');
629             free ( (void*)pz );
630             if (skip)
631               {
632                 p_fixd->fd_flags |= FD_SKIP_TEST;
633                 continue;
634               }
635            }
636         }
637
638       /* FOR every test for the fixup, ...  */
639
640       while (--test_ct >= 0)
641         {
642           switch (p_test->type)
643             {
644             case TT_EGREP:
645             case TT_NEGREP:
646               /*  You might consider putting the following under #ifdef.
647                   The number of re's used is computed by autogen.
648                   So, it is static and known at compile time.  */
649
650               if (--re_ct < 0)
651                 {
652                   fputs ("out of RE's\n", stderr);
653                   exit (EXIT_FAILURE);
654                 }
655
656               p_test->p_test_regex = p_re++;
657               pz_err = re_compile_pattern (p_test->pz_test_text,
658                                           strlen (p_test->pz_test_text),
659                                           p_test->p_test_regex);
660               if (pz_err != (char *) NULL)
661                 {
662                   fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name,
663                            p_test->pz_test_text, pz_err);
664                   exit (EXIT_FAILURE);
665                 }
666             }
667           p_test++;
668         }
669     }
670   while (p_fixd++, --fix_ct > 0);
671 }
672
673
674 /* * * * * * * * * * * * *
675  
676    create_file  Create the output modified file.
677    Input:    the name of the file to create
678    Returns:  a file pointer to the new, open file  */
679
680 #define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
681
682 FILE *
683 create_file (pz_file_name)
684      const char *pz_file_name;
685 {
686   int fd;
687   FILE *pf;
688   char fname[MAXPATHLEN];
689
690 #ifdef DEBUG
691   if (strncmp( pz_file_name, pz_find_base, find_base_len ) != 0)
692     {
693       fprintf (stderr, "Error:  input file %s does not match %s/*\n",
694                pz_file_name, pz_find_base );
695       exit (1);
696     }
697 #endif
698
699   sprintf (fname, "%s/%s", pz_dest_dir, pz_file_name + find_base_len);
700
701   fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
702
703   /*  We may need to create the directories needed... */
704   if ((fd < 0) && (errno == ENOENT))
705     {
706       char *pz_dir = strchr (fname + 1, '/');
707       struct stat stbf;
708
709       while (pz_dir != (char *) NULL)
710         {
711           *pz_dir = NUL;
712           if (stat (fname, &stbf) < 0)
713             {
714               mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
715                      | S_IROTH | S_IXOTH);
716             }
717
718           *pz_dir = '/';
719           pz_dir = strchr (pz_dir + 1, '/');
720         }
721
722       /*  Now, lets try the open again... */
723       fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
724     }
725   if (fd < 0)
726     {
727       fprintf (stderr, "Error %d (%s) creating %s\n",
728                errno, strerror (errno), fname);
729       exit (EXIT_FAILURE);
730     }
731   fprintf (stderr, "Fixed:  %s\n", pz_file_name);
732   pf = fdopen (fd, "w");
733
734 #ifdef LATER
735   {
736     static const char hdr[] =
737     "/*  DO NOT EDIT THIS FILE.\n\n"
738     "    It has been auto-edited by fixincludes from /usr/include/%s\n"
739     "    This had to be done to correct non-standard usages in the\n"
740     "    original, manufacturer supplied header file.  */\n\n";
741
742     fprintf (pf, hdr, pz_file_name);
743   }
744 #endif
745   return pf;
746 }
747
748
749 /* * * * * * * * * * * * *
750
751   test_test   make sure a shell-style test expression passes.
752   Input:  a pointer to the descriptor of the test to run and
753           the name of the file that we might want to fix
754   Result: SUCCESS or FAILURE, depending on the result of the
755           shell script we run.  */
756
757 t_success
758 test_test (p_test, pz_file_name)
759      tTestDesc *p_test;
760      char*      pz_file_name;
761 {
762   tSCC cmd_fmt[] =
763 "file=%s\n\
764 if ( test %s ) > /dev/null 2>&1\n\
765 then echo TRUE\n\
766 else echo FALSE\n\
767 fi";
768
769   char *pz_res;
770   t_success res = FAILURE;
771
772   static char cmd_buf[4096];
773
774   sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text);
775   pz_res = run_shell (cmd_buf);
776   if (*pz_res == 'T')
777     res = SUCCESS;
778   free ((void *) pz_res);
779   return res;
780 }
781
782
783 /* * * * * * * * * * * * *
784  
785   egrep_test   make sure an egrep expression is found in the file text.
786   Input:  a pointer to the descriptor of the test to run and
787           the pointer to the contents of the file under suspicion
788   Result: SUCCESS if the pattern is found, FAILURE otherwise
789
790   The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test
791   is inverted.  */
792
793 t_success
794 egrep_test (pz_data, p_test)
795      char *pz_data;
796      tTestDesc *p_test;
797 {
798   regmatch_t match;
799
800 #ifndef NO_BOGOSITY
801   if (p_test->p_test_regex == 0)
802     fprintf (stderr, "fixincl ERROR RE not compiled:  `%s'\n",
803              p_test->pz_test_text);
804 #endif
805   if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
806     return SUCCESS;
807   return FAILURE;
808 }
809
810
811 /* * * * * * * * * * * * *
812
813   quoted_file_exists  Make sure that a file exists before we emit
814   the file name.  If we emit the name, our invoking shell will try
815   to copy a non-existing file into the destination directory.  */
816
817 int
818 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
819      char* pz_src_path;
820      char* pz_file_path;
821      char* pz_file;
822 {
823   char z[ MAXPATHLEN ];
824   char* pz;
825   sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
826   pz = z + strlen ( z );
827
828   for (;;) {
829     char ch = *pz_file++;
830     if (! isgraph( ch ))
831       return 0;
832     if (ch == '"')
833       break;
834     *pz++ = ch;
835   }
836   *pz = '\0';
837   {
838     struct stat s;
839     if (stat (z, &s) != 0)
840       return 0;
841     return S_ISREG( s.st_mode );
842   }
843 }
844
845
846 /* * * * * * * * * * * * *
847  *
848    extract_quoted_files
849   
850    The syntax, `#include "file.h"' specifies that the compiler is to
851    search the local directory of the current file before the include
852    list.  Consequently, if we have modified a header and stored it in
853    another directory, any files that are included by that modified
854    file in that fashion must also be copied into this new directory.
855    This routine finds those flavors of #include and for each one found
856    emits a triple of:
857   
858     1.  source directory of the original file
859     2.  the relative path file name of the #includ-ed file
860     3.  the full destination path for this file
861
862    Input:  the text of the file, the file name and a pointer to the
863            match list where the match information was stored.
864    Result: internally nothing.  The results are written to stdout
865            for interpretation by the invoking shell  */
866
867
868 void
869 extract_quoted_files (pz_data, pz_file_name, p_re_match)
870      char *pz_data;
871      const char *pz_file_name;
872      regmatch_t *p_re_match;
873 {
874   char *pz_dir_end = strrchr (pz_file_name, '/');
875   char *pz_incl_quot = pz_data;
876
877   fprintf (stderr, "Quoted includes in %s\n", pz_file_name);
878
879   /*  Set "pz_file_name" to point to the containing subdirectory of the source
880       If there is none, then it is in our current directory, ".".   */
881
882   if (pz_dir_end == (char *) NULL)
883     pz_file_name = ".";
884   else
885     *pz_dir_end = '\0';
886
887   for (;;)
888     {
889       pz_incl_quot += p_re_match->rm_so;
890
891       /*  Skip forward to the included file name */
892       while (isspace (*pz_incl_quot))
893         pz_incl_quot++;
894       while (isspace (*++pz_incl_quot))
895         ;
896       pz_incl_quot += sizeof ("include") - 1;
897       while (*pz_incl_quot++ != '"')
898         ;
899
900       if (quoted_file_exists (pz_src_dir, pz_file_name, pz_incl_quot))
901         {
902           /* Print the source directory and the subdirectory
903              of the file in question.  */
904           printf ("%s  %s/", pz_src_dir, pz_file_name);
905           pz_dir_end = pz_incl_quot;
906
907           /* Append to the directory the relative path of the desired file */
908           while (*pz_incl_quot != '"')
909             putc (*pz_incl_quot++, stdout);
910
911           /* Now print the destination directory appended with the
912              relative path of the desired file */
913           printf ("  %s/%s/", pz_dest_dir, pz_file_name);
914           while (*pz_dir_end != '"')
915             putc (*pz_dir_end++, stdout);
916
917           /* End of entry */
918           putc ('\n', stdout);
919         }
920
921       /* Find the next entry */
922       if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
923         break;
924     }
925 }
926
927
928 /* * * * * * * * * * * * *
929
930    Process the potential fixes for a particular include file.
931    Input:  the original text of the file and the file's name
932    Result: none.  A new file may or may not be created.  */
933
934 void
935 process (pz_data, pz_file_name)
936      char *pz_data;
937      const char *pz_file_name;
938 {
939   static char env_current_file[1024];
940   tFixDesc *p_fixd = fixDescList;
941   int todo_ct = FIX_COUNT;
942   int read_fd = -1;
943   int num_children = 0;
944
945   /*  IF this is the first time through,
946       THEN put the 'file' environment variable into the environment.
947            This is used by some of the subject shell scripts and tests.   */
948
949   if (env_current_file[0] == NUL) {
950     strcpy (env_current_file, "file=");
951     putenv (env_current_file);
952   }
953
954   /*
955      Ghastly as it is, this actually updates the value of the variable:
956    
957        putenv(3C)             C Library Functions             putenv(3C)
958    
959        DESCRIPTION
960             putenv() makes the value of the  environment  variable  name
961             equal  to value by altering an existing variable or creating
962             a new one.  In either case, the string pointed to by  string
963             becomes part of the environment, so altering the string will
964             change the environment.  string points to a  string  of  the
965             form  ``name=value.''  The space used by string is no longer
966             used once a new string-defining name is passed to putenv().
967    */
968   strcpy (env_current_file + 5, pz_file_name);
969   process_chain_head = NOPROCESS;
970   fprintf (stderr, "%-50s   \r", pz_file_name );
971   /* For every fix in our fix list, ...  */
972   for (; todo_ct > 0; p_fixd++, todo_ct--)
973     {
974       tTestDesc *p_test;
975       int test_ct;
976
977       if (p_fixd->fd_flags & FD_SKIP_TEST)
978         continue;
979
980       /*  IF there is a file name restriction,
981           THEN ensure the current file name matches one in the pattern  */
982
983       if (p_fixd->file_list != (char *) NULL)
984         {
985           const char *pz_fname = pz_file_name;
986           const char *pz_scan = p_fixd->file_list;
987           size_t name_len;
988
989           while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
990             pz_fname += 2;
991           name_len = strlen (pz_fname);
992
993           for (;;)
994             {
995               pz_scan = strstr (pz_scan + 1, pz_fname);
996               /*  IF we can't match the string at all,
997                   THEN bail  */
998               if (pz_scan == (char *) NULL)
999                 goto next_fix;
1000
1001               /*  IF the match is surrounded by the '|' markers,
1002                   THEN we found a full match -- time to run the tests  */
1003
1004               if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1005                 break;
1006             }
1007         }
1008
1009       /*  FOR each test, see if it fails.
1010           IF it does fail, then we go on to the next test */
1011
1012       for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1013            test_ct-- > 0;
1014            p_test++)
1015         {
1016 #ifdef DEBUG_TEST
1017           static const char z_test_fail[] =
1018             "%16s test %2d failed for %s\n";
1019 #endif
1020           switch (p_test->type)
1021             {
1022             case TT_TEST:
1023               if (!SUCCESSFUL (test_test (p_test, pz_file_name)))
1024                 {
1025 #ifdef DEBUG_TEST
1026                   fprintf (stderr, z_test_fail, p_fixd->fix_name,
1027                            p_fixd->test_ct - test_ct, pz_file_name);
1028 #endif
1029                   goto next_fix;
1030                 }
1031               break;
1032
1033             case TT_EGREP:
1034               if (!SUCCESSFUL (egrep_test (pz_data, p_test)))
1035                 {
1036 #ifdef DEBUG_TEST
1037                   fprintf (stderr, z_test_fail, p_fixd->fix_name,
1038                            p_fixd->test_ct - test_ct, pz_file_name);
1039 #endif
1040                   goto next_fix;
1041                 }
1042               break;
1043
1044             case TT_NEGREP:
1045               if (SUCCESSFUL (egrep_test (pz_data, p_test)))
1046                 {
1047 #ifdef DEBUG_TEST
1048                   fprintf (stderr, z_test_fail, p_fixd->fix_name,
1049                            p_fixd->test_ct - test_ct, pz_file_name);
1050 #endif
1051                   goto next_fix;
1052                 }
1053               break;
1054             }
1055         }
1056
1057       fprintf (stderr, "Applying %-24s to %s\n",
1058                p_fixd->fix_name, pz_file_name);
1059
1060       /*  IF we do not have a read pointer,
1061           THEN this is the first fix for the current file.
1062           Open the source file.  That will be used as stdin for
1063           the first fix.  Any subsequent fixes will use the
1064           stdout descriptor of the previous fix as its stdin.  */
1065
1066       if (read_fd == -1)
1067         {
1068           read_fd = open (pz_file_name, O_RDONLY);
1069           if (read_fd < 0)
1070             {
1071               fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1072                        strerror (errno), pz_file_name);
1073               exit (EXIT_FAILURE);
1074             }
1075         }
1076
1077       /*  This loop should only cycle for 1/2 of one loop.
1078           "chain_open" starts a process that uses "read_fd" as
1079           its stdin and returns the new fd this process will use
1080           for stdout.  */
1081
1082       for (;;)
1083         {
1084           tSCC z_err[] = "Error %d (%s) starting filter process for %s\n";
1085           static int failCt = 0;
1086           int fd = chain_open (read_fd,
1087                                (t_pchar *) p_fixd->patch_args,
1088                                (process_chain_head == -1)
1089                                ? &process_chain_head : (pid_t *) NULL);
1090
1091           if (fd != -1)
1092             {
1093               read_fd = fd;
1094               num_children++;
1095               break;
1096             }
1097
1098           fprintf (stderr, z_err, errno, strerror (errno),
1099                    p_fixd->fix_name);
1100
1101           if ((errno != EAGAIN) || (++failCt > 10))
1102             exit (EXIT_FAILURE);
1103           sleep (1);
1104         }
1105
1106     next_fix:
1107       ;
1108     }
1109
1110   /*  IF after all the tests we did not start any patch programs,
1111       THEN quit now.   */
1112
1113   if (read_fd < 0)
1114     return;
1115
1116   /*  OK.  We have work to do.  Read back in the output
1117       of the filtering chain.  Compare each byte as we read it with
1118       the contents of the original file.  As soon as we find any
1119       difference, we will create the output file, write out all
1120       the matched text and then copy any remaining data from the
1121       output of the filter chain.
1122       */
1123   {
1124     FILE *in_fp = fdopen (read_fd, "r");
1125     FILE *out_fp = (FILE *) NULL;
1126     char *pz_cmp = pz_data;
1127
1128     for (;;)
1129       {
1130         int ch;
1131
1132         ch = getc (in_fp);
1133         if (ch == EOF)
1134           break;
1135
1136         /*  IF we are emitting the output
1137             THEN emit this character, too.
1138             */
1139         if (out_fp != (FILE *) NULL)
1140           putc (ch, out_fp);
1141
1142         /*  ELSE if this character does not match the original,
1143             THEN now is the time to start the output.
1144             */
1145         else if (ch != *pz_cmp)
1146           {
1147             out_fp = create_file (pz_file_name);
1148
1149             /*  IF there are matched data, write it all now. */
1150             if (pz_cmp != pz_data)
1151               {
1152                 char c = *pz_cmp;
1153                 
1154                 *pz_cmp = NUL;
1155                 fputs (pz_data, out_fp);
1156                 *pz_cmp = c;
1157               }
1158
1159             /*  Emit the current unmatching character */
1160             putc (ch, out_fp);
1161           }
1162         else
1163           /*  ELSE the character matches.  Advance the compare ptr */
1164           pz_cmp++;
1165       }
1166
1167     /*  IF we created the output file, ... */
1168     if (out_fp != (FILE *) NULL)
1169       {
1170         regmatch_t match;
1171
1172         /* Close the file and see if we have to worry about
1173            `#include "file.h"' constructs.  */
1174         fclose (out_fp);
1175         if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0)
1176           extract_quoted_files (pz_data, pz_file_name, &match);
1177       }
1178     fclose (in_fp);
1179   }
1180   close (read_fd);  /* probably redundant, but I'm paranoid */
1181
1182   /* Wait for child processes created by chain_open()
1183      to avoid creating zombies.  */
1184   while (--num_children >= 0)
1185     wait ((int *) NULL);
1186 }