OSDN Git Service

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