OSDN Git Service

static initialization removal & K&R-ification
[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   /*
283      Here we are the child of the grandparent process.  The parent
284      of all the little fixup processes.  We ignore the deaths of
285      our children.  */
286
287   signal (SIGCLD,  SIG_IGN);
288
289   /*  For every file specified in stdandard in
290       (except as throttled for bogus reasons)...
291       */
292   for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++)
293     {
294       char *pz_data;
295       char *pz_file_name = apz_names[loop_ct];
296
297       if (access (pz_file_name, R_OK) != 0)
298         {
299           int erno = errno;
300           fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
301                    pz_file_name, getcwd ((char *) NULL, MAXPATHLEN),
302                    erno, strerror (erno));
303         }
304       else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL))
305         {
306           if (strstr (pz_data, gnu_lib_mark) == (char *) NULL)
307             process (pz_data, pz_file_name);
308           free ((void *) pz_data);
309         }
310     }
311
312   return EXIT_SUCCESS;
313 }
314
315
316 /* * * * * * * * * * * * */
317
318 void
319 initialize()
320 {
321   static const char var_not_found[] =
322     "fixincl ERROR:  %s environment variable not defined\n";
323
324   {
325     static const char var[] = "TARGET_MACHINE";
326     pz_machine = getenv (var);
327     if (pz_machine == (char *) NULL)
328       {
329         fprintf (stderr, var_not_found, var);
330         exit (EXIT_FAILURE);
331       }
332   }
333
334   {
335     static const char var[] = "DESTDIR";
336     pz_dest_dir = getenv (var);
337     if (pz_dest_dir == (char *) NULL)
338       {
339         fprintf (stderr, var_not_found, var);
340         exit (EXIT_FAILURE);
341       }
342   }
343
344   {
345     static const char var[] = "SRCDIR";
346     pz_src_dir = getenv (var);
347     if (pz_src_dir == (char *) NULL)
348       {
349         fprintf (stderr, var_not_found, var);
350         exit (EXIT_FAILURE);
351       }
352   }
353
354   {
355     static const char var[] = "FIND_BASE";
356     pz_find_base = getenv (var);
357     if (pz_find_base == (char *) NULL)
358       {
359         fprintf (stderr, var_not_found, var);
360         exit (EXIT_FAILURE);
361       }
362     find_base_len = strlen( pz_find_base );
363   }
364
365   /*  Compile all the regular expressions now.
366       That way, it is done only once for the whole run.
367       */
368   run_compiles ();
369
370   signal (SIGQUIT, SIG_IGN);
371   signal (SIGIOT,  SIG_IGN);
372   signal (SIGPIPE, SIG_IGN);
373   signal (SIGALRM, SIG_IGN);
374   signal (SIGTERM, SIG_IGN);
375
376   /*
377      Make sure that if we opened a server process, we close it now.
378      This is the grandparent process.  We don't need the server anymore
379      and our children should make their own.  */
380
381   close_server ();
382   (void)wait ( (int*)NULL );
383 }
384
385 /* * * * * * * * * * * * *
386  
387    wait_for_pid  -  Keep calling `wait(2)' until it returns
388    the process id we are looking for.  Not every system has
389    `waitpid(2)'.  We also ensure that the children exit with success. */
390
391 void
392 wait_for_pid(child, file_name_ct)
393      pid_t child;
394      int file_name_ct;
395 {
396   for (;;) {
397     int status;
398     pid_t dead_kid = wait (&status);
399
400     if (dead_kid == child)
401       {
402         if (! WIFEXITED( status ))
403           {
404             fprintf (stderr, "child process %d is hung on signal %d\n",
405                      child, WSTOPSIG( status ));
406             exit (EXIT_FAILURE);
407           }
408         if (WEXITSTATUS( status ) != 0)
409           {
410             fprintf (stderr, "child process %d exited with status %d\n",
411                      child, WEXITSTATUS( status ));
412             exit (EXIT_FAILURE);
413           }
414         break; /* normal child completion */
415       }
416
417     /*
418        IF there is an error, THEN see if it is retryable.
419        If it is not retryable, then break out of this loop.  */
420     if (dead_kid == NOPROCESS)
421       {
422         switch (errno) {
423         case EINTR:
424         case EAGAIN:
425           break;
426
427         default:
428           fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
429                    errno, strerror( errno ), child );
430           /* FALLTHROUGH */
431
432         case ECHILD: /* no children to wait for?? */
433           return;
434         }
435       }
436   } done_waiting:;
437 }
438
439
440 /* * * * * * * * * * * * *
441  
442    load_file loads all the contents of a file into malloc-ed memory.
443    Its argument is the name of the file to read in; the returned
444    result is the NUL terminated contents of the file.  The file
445    is presumed to be an ASCII text file containing no NULs.  */
446 char *
447 load_file (pz_file_name)
448      const char *pz_file_name;
449 {
450   char *pz_data;
451   size_t file_size;
452
453   {
454     struct stat stbf;
455     
456     if (stat (pz_file_name, &stbf) != 0)
457       {
458         fprintf (stderr, "error %d (%s) stat-ing %s\n",
459                  errno, strerror (errno), pz_file_name);
460         return (char *) NULL;
461       }
462     file_size = stbf.st_size;
463   }
464   if (file_size == 0)
465     return (char *) NULL;
466
467   pz_data = (char *) malloc ((file_size + 16) & ~0x00F);
468   if (pz_data == (char *) NULL)
469     {
470       fprintf (stderr, "error:  could not malloc %d bytes\n",
471                file_size);
472       exit (EXIT_FAILURE);
473     }
474
475   {
476     FILE *fp = fopen (pz_file_name, "r");
477     size_t size_left = file_size;
478     char *read_ptr = pz_data;
479
480     if (fp == (FILE *) NULL)
481       {
482         fprintf (stderr, "error %d (%s) opening %s\n", errno,
483                  strerror (errno), pz_file_name);
484         free ((void *) pz_data);
485         return (char *) NULL;
486       }
487
488     do
489       {
490         size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp);
491
492         if (sizeRead == 0)
493           {
494             if (feof (fp))
495               break;
496
497             if (ferror (fp))
498               {
499                 int err = errno;
500                 if (err != EISDIR)
501                   fprintf (stderr, "error %d (%s) reading %s\n", err,
502                            strerror (err), pz_file_name);
503                 free ((void *) pz_data);
504                 fclose (fp);
505                 return (char *) NULL;
506               }
507           }
508
509         read_ptr += sizeRead;
510         size_left -= sizeRead;
511       }
512     while (size_left != 0);
513
514     *read_ptr = '\0';
515     fclose (fp);
516   }
517   return pz_data;
518 }
519
520
521 /* * * * * * * * * * * * *
522  
523    run_compiles   run all the regexp compiles for all the fixes once.
524  */
525 void
526 run_compiles ()
527 {
528   tSCC z_bad_comp[] = "fixincl ERROR:  cannot compile %s regex for %s\n\
529 \texpr = `%s'\n\terror %s\n";
530   tFixDesc *p_fixd = fixDescList;
531   int fix_ct = FIX_COUNT;
532   tTestDesc *p_test;
533   int test_ct;
534   int re_ct = REGEX_COUNT;
535   const char *pz_err;
536   regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
537
538   if (p_re == (regex_t *) NULL)
539     {
540       fprintf (stderr, "fixincl ERROR:  cannot allocate %d bytes for regex\n",
541                REGEX_COUNT * sizeof (regex_t));
542       exit (EXIT_FAILURE);
543     }
544
545   /*  Make sure re_compile_pattern does not stumble across invalid
546       data */
547
548   memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
549   memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
550
551   /*  The patterns we search for are all egrep patterns.
552       In the shell version of this program, we invoke egrep
553       with the supplied pattern.  Here, we will run
554       re_compile_pattern, but it must be using the same rules.  */
555
556   re_set_syntax (RE_SYNTAX_EGREP);
557   pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
558                               &incl_quote_re);
559   if (pz_err != (char *) NULL)
560     {
561       fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
562                incl_quote_pat, pz_err);
563       exit (EXIT_FAILURE);
564     }
565
566   /* FOR every fixup, ...  */
567   do
568     {
569       p_test = p_fixd->p_test_desc;
570       test_ct = p_fixd->test_ct;
571
572       /*  IF the machine type pointer is not NULL (we are not in test mode)
573              AND this test is for or not done on particular machines
574           THEN ...   */
575
576       if (  (pz_machine != NULL)
577          && (p_fixd->papz_machs != (const char**) NULL) )
578         {
579           const char **papz_machs = p_fixd->papz_machs;
580           char *pz = file_name_buf;
581           char *pz_sep = "";
582           tCC *pz_if_true;
583           tCC *pz_if_false;
584           tSCC skip[] = "skip";
585           tSCC run[] = "run";
586
587           /*  Construct a shell script that looks like this:
588
589               case our-cpu-platform-os in
590               tests-cpu-platform-os-pattern )
591                   echo run ;;
592               * )
593                   echo skip ;;
594               esac
595
596               where 'run' and 'skip' may be reversed, depending on
597               the sense of the test.  */
598
599           sprintf (pz, "case %s in\n", pz_machine);
600           pz += strlen (pz);
601
602           if (p_fixd->fd_flags & FD_MACH_IFNOT)
603             {
604               pz_if_true  = skip;
605               pz_if_false = run;
606             }
607           else
608             {
609               pz_if_true  = run;
610               pz_if_false = skip;
611             }
612
613           /*  FOR any additional machine names to test for,
614               insert the " | \\\n" glue and the machine pattern.  */
615
616           for (;;)
617             {
618               const char* pz_mach = *(papz_machs++);
619
620               if (pz_mach == (const char*) NULL)
621                 break;
622               sprintf (pz, "%s  %s", pz_sep, pz_mach);
623               pz += strlen (pz);
624               pz_sep = " | \\\n";
625             }
626           sprintf (pz, " )\n    echo %s ;;\n  * )\n    echo %s ;;\nesac",
627                    pz_if_true, pz_if_false);
628
629           /*  Run the script.
630               The result will start either with 's' or 'r'.  */
631
632           {
633             int skip;
634             pz = run_shell (file_name_buf);
635             skip = (*pz == 's');
636             free ( (void*)pz );
637             if (skip)
638               {
639                 p_fixd->fd_flags |= FD_SKIP_TEST;
640                 continue;
641               }
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   tSCC cmd_fmt[] =
770 "file=%s\n\
771 if ( test %s ) > /dev/null 2>&1\n\
772 then echo TRUE\n\
773 else echo FALSE\n\
774 fi";
775
776   char *pz_res;
777   t_success res = FAILURE;
778
779   static char cmd_buf[4096];
780
781   sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text);
782   pz_res = run_shell (cmd_buf);
783   if (*pz_res == 'T')
784     res = SUCCESS;
785   free ((void *) pz_res);
786   return res;
787 }
788
789
790 /* * * * * * * * * * * * *
791  
792   egrep_test   make sure an egrep expression is found in the file text.
793   Input:  a pointer to the descriptor of the test to run and
794           the pointer to the contents of the file under suspicion
795   Result: SUCCESS if the pattern is found, FAILURE otherwise
796
797   The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test
798   is inverted.  */
799
800 t_success
801 egrep_test (pz_data, p_test)
802      char *pz_data;
803      tTestDesc *p_test;
804 {
805   regmatch_t match;
806
807 #ifndef NO_BOGOSITY
808   if (p_test->p_test_regex == 0)
809     fprintf (stderr, "fixincl ERROR RE not compiled:  `%s'\n",
810              p_test->pz_test_text);
811 #endif
812   if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
813     return SUCCESS;
814   return FAILURE;
815 }
816
817
818 /* * * * * * * * * * * * *
819
820   quoted_file_exists  Make sure that a file exists before we emit
821   the file name.  If we emit the name, our invoking shell will try
822   to copy a non-existing file into the destination directory.  */
823
824 int
825 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
826      char* pz_src_path;
827      char* pz_file_path;
828      char* pz_file;
829 {
830   char z[ MAXPATHLEN ];
831   char* pz;
832   sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
833   pz = z + strlen ( z );
834
835   for (;;) {
836     char ch = *pz_file++;
837     if (! isgraph( ch ))
838       return 0;
839     if (ch == '"')
840       break;
841     *pz++ = ch;
842   }
843   *pz = '\0';
844   {
845     struct stat s;
846     if (stat (z, &s) != 0)
847       return 0;
848     return S_ISREG( s.st_mode );
849   }
850 }
851
852
853 /* * * * * * * * * * * * *
854  *
855    extract_quoted_files
856   
857    The syntax, `#include "file.h"' specifies that the compiler is to
858    search the local directory of the current file before the include
859    list.  Consequently, if we have modified a header and stored it in
860    another directory, any files that are included by that modified
861    file in that fashion must also be copied into this new directory.
862    This routine finds those flavors of #include and for each one found
863    emits a triple of:
864   
865     1.  source directory of the original file
866     2.  the relative path file name of the #includ-ed file
867     3.  the full destination path for this file
868
869    Input:  the text of the file, the file name and a pointer to the
870            match list where the match information was stored.
871    Result: internally nothing.  The results are written to stdout
872            for interpretation by the invoking shell  */
873
874
875 void
876 extract_quoted_files (pz_data, pz_file_name, p_re_match)
877      char *pz_data;
878      const char *pz_file_name;
879      regmatch_t *p_re_match;
880 {
881   char *pz_dir_end = strrchr (pz_file_name, '/');
882   char *pz_incl_quot = pz_data;
883
884   fprintf (stderr, "Quoted includes in %s\n", pz_file_name);
885
886   /*  Set "pz_file_name" to point to the containing subdirectory of the source
887       If there is none, then it is in our current directory, ".".   */
888
889   if (pz_dir_end == (char *) NULL)
890     pz_file_name = ".";
891   else
892     *pz_dir_end = '\0';
893
894   for (;;)
895     {
896       pz_incl_quot += p_re_match->rm_so;
897
898       /*  Skip forward to the included file name */
899       while (isspace (*pz_incl_quot))
900         pz_incl_quot++;
901       while (isspace (*++pz_incl_quot))
902         ;
903       pz_incl_quot += sizeof ("include") - 1;
904       while (*pz_incl_quot++ != '"')
905         ;
906
907       if (quoted_file_exists (pz_src_dir, pz_file_name, pz_incl_quot))
908         {
909           /* Print the source directory and the subdirectory
910              of the file in question.  */
911           printf ("%s  %s/", pz_src_dir, pz_file_name);
912           pz_dir_end = pz_incl_quot;
913
914           /* Append to the directory the relative path of the desired file */
915           while (*pz_incl_quot != '"')
916             putc (*pz_incl_quot++, stdout);
917
918           /* Now print the destination directory appended with the
919              relative path of the desired file */
920           printf ("  %s/%s/", pz_dest_dir, pz_file_name);
921           while (*pz_dir_end != '"')
922             putc (*pz_dir_end++, stdout);
923
924           /* End of entry */
925           putc ('\n', stdout);
926         }
927
928       /* Find the next entry */
929       if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
930         break;
931     }
932 }
933
934
935 /* * * * * * * * * * * * *
936
937    Process the potential fixes for a particular include file.
938    Input:  the original text of the file and the file's name
939    Result: none.  A new file may or may not be created.  */
940
941 void
942 process (pz_data, pz_file_name)
943      char *pz_data;
944      const char *pz_file_name;
945 {
946   static char env_current_file[1024];
947   tFixDesc *p_fixd = fixDescList;
948   int todo_ct = FIX_COUNT;
949   int read_fd = -1;
950   int num_children = 0;
951
952   /*  IF this is the first time through,
953       THEN put the 'file' environment variable into the environment.
954            This is used by some of the subject shell scripts and tests.   */
955
956   if (env_current_file[0] == NUL) {
957     strcpy (env_current_file, "file=");
958     putenv (env_current_file);
959   }
960
961   /*
962      Ghastly as it is, this actually updates the value of the variable:
963    
964        putenv(3C)             C Library Functions             putenv(3C)
965    
966        DESCRIPTION
967             putenv() makes the value of the  environment  variable  name
968             equal  to value by altering an existing variable or creating
969             a new one.  In either case, the string pointed to by  string
970             becomes part of the environment, so altering the string will
971             change the environment.  string points to a  string  of  the
972             form  ``name=value.''  The space used by string is no longer
973             used once a new string-defining name is passed to putenv().
974    */
975   strcpy (env_current_file + 5, pz_file_name);
976   process_chain_head = NOPROCESS;
977   fprintf (stderr, "%-50s   \r", pz_file_name );
978   /* For every fix in our fix list, ...  */
979   for (; todo_ct > 0; p_fixd++, todo_ct--)
980     {
981       tTestDesc *p_test;
982       int test_ct;
983
984       if (p_fixd->fd_flags & FD_SKIP_TEST)
985         continue;
986
987       /*  IF there is a file name restriction,
988           THEN ensure the current file name matches one in the pattern  */
989
990       if (p_fixd->file_list != (char *) NULL)
991         {
992           const char *pz_fname = pz_file_name;
993           const char *pz_scan = p_fixd->file_list;
994           size_t name_len;
995
996           while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
997             pz_fname += 2;
998           name_len = strlen (pz_fname);
999
1000           for (;;)
1001             {
1002               pz_scan = strstr (pz_scan + 1, pz_fname);
1003               /*  IF we can't match the string at all,
1004                   THEN bail  */
1005               if (pz_scan == (char *) NULL)
1006                 goto next_fix;
1007
1008               /*  IF the match is surrounded by the '|' markers,
1009                   THEN we found a full match -- time to run the tests  */
1010
1011               if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1012                 break;
1013             }
1014         }
1015
1016       /*  FOR each test, see if it fails.
1017           IF it does fail, then we go on to the next test */
1018
1019       for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1020            test_ct-- > 0;
1021            p_test++)
1022         {
1023 #ifdef DEBUG_TEST
1024           static const char z_test_fail[] =
1025             "%16s test %2d failed for %s\n";
1026 #endif
1027           switch (p_test->type)
1028             {
1029             case TT_TEST:
1030               if (!SUCCESSFUL (test_test (p_test, pz_file_name)))
1031                 {
1032 #ifdef DEBUG_TEST
1033                   fprintf (stderr, z_test_fail, p_fixd->fix_name,
1034                            p_fixd->test_ct - test_ct, pz_file_name);
1035 #endif
1036                   goto next_fix;
1037                 }
1038               break;
1039
1040             case TT_EGREP:
1041               if (!SUCCESSFUL (egrep_test (pz_data, p_test)))
1042                 {
1043 #ifdef DEBUG_TEST
1044                   fprintf (stderr, z_test_fail, p_fixd->fix_name,
1045                            p_fixd->test_ct - test_ct, pz_file_name);
1046 #endif
1047                   goto next_fix;
1048                 }
1049               break;
1050
1051             case TT_NEGREP:
1052               if (SUCCESSFUL (egrep_test (pz_data, p_test)))
1053                 {
1054 #ifdef DEBUG_TEST
1055                   fprintf (stderr, z_test_fail, p_fixd->fix_name,
1056                            p_fixd->test_ct - test_ct, pz_file_name);
1057 #endif
1058                   goto next_fix;
1059                 }
1060               break;
1061             }
1062         }
1063
1064       fprintf (stderr, "Applying %-24s to %s\n",
1065                p_fixd->fix_name, pz_file_name);
1066
1067       /*  IF we do not have a read pointer,
1068           THEN this is the first fix for the current file.
1069           Open the source file.  That will be used as stdin for
1070           the first fix.  Any subsequent fixes will use the
1071           stdout descriptor of the previous fix as its stdin.  */
1072
1073       if (read_fd == -1)
1074         {
1075           read_fd = open (pz_file_name, O_RDONLY);
1076           if (read_fd < 0)
1077             {
1078               fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1079                        strerror (errno), pz_file_name);
1080               exit (EXIT_FAILURE);
1081             }
1082         }
1083
1084       /*  This loop should only cycle for 1/2 of one loop.
1085           "chain_open" starts a process that uses "read_fd" as
1086           its stdin and returns the new fd this process will use
1087           for stdout.  */
1088
1089       for (;;)
1090         {
1091           tSCC z_err[] = "Error %d (%s) starting filter process for %s\n";
1092           static int failCt = 0;
1093           int fd = chain_open (read_fd,
1094                                (t_pchar *) p_fixd->patch_args,
1095                                (process_chain_head == -1)
1096                                ? &process_chain_head : (pid_t *) NULL);
1097
1098           if (fd != -1)
1099             {
1100               read_fd = fd;
1101               num_children++;
1102               break;
1103             }
1104
1105           fprintf (stderr, z_err, errno, strerror (errno),
1106                    p_fixd->fix_name);
1107
1108           if ((errno != EAGAIN) || (++failCt > 10))
1109             exit (EXIT_FAILURE);
1110           sleep (1);
1111         }
1112
1113     next_fix:
1114       ;
1115     }
1116
1117   /*  IF after all the tests we did not start any patch programs,
1118       THEN quit now.   */
1119
1120   if (read_fd < 0)
1121     return;
1122
1123   /*  OK.  We have work to do.  Read back in the output
1124       of the filtering chain.  Compare each byte as we read it with
1125       the contents of the original file.  As soon as we find any
1126       difference, we will create the output file, write out all
1127       the matched text and then copy any remaining data from the
1128       output of the filter chain.
1129       */
1130   {
1131     FILE *in_fp = fdopen (read_fd, "r");
1132     FILE *out_fp = (FILE *) NULL;
1133     char *pz_cmp = pz_data;
1134
1135     for (;;)
1136       {
1137         int ch;
1138
1139         ch = getc (in_fp);
1140         if (ch == EOF)
1141           break;
1142
1143         /*  IF we are emitting the output
1144             THEN emit this character, too.
1145             */
1146         if (out_fp != (FILE *) NULL)
1147           putc (ch, out_fp);
1148
1149         /*  ELSE if this character does not match the original,
1150             THEN now is the time to start the output.
1151             */
1152         else if (ch != *pz_cmp)
1153           {
1154             out_fp = create_file (pz_file_name);
1155
1156             /*  IF there are matched data, write it all now. */
1157             if (pz_cmp != pz_data)
1158               {
1159                 char c = *pz_cmp;
1160                 
1161                 *pz_cmp = NUL;
1162                 fputs (pz_data, out_fp);
1163                 *pz_cmp = c;
1164               }
1165
1166             /*  Emit the current unmatching character */
1167             putc (ch, out_fp);
1168           }
1169         else
1170           /*  ELSE the character matches.  Advance the compare ptr */
1171           pz_cmp++;
1172       }
1173
1174     /*  IF we created the output file, ... */
1175     if (out_fp != (FILE *) NULL)
1176       {
1177         regmatch_t match;
1178
1179         /* Close the file and see if we have to worry about
1180            `#include "file.h"' constructs.  */
1181         fclose (out_fp);
1182         if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0)
1183           extract_quoted_files (pz_data, pz_file_name, &match);
1184       }
1185     fclose (in_fp);
1186   }
1187   close (read_fd);  /* probably redundant, but I'm paranoid */
1188
1189   /* Wait for child processes created by chain_open()
1190      to avoid creating zombies.  */
1191   while (--num_children >= 0)
1192     wait ((int *) NULL);
1193 }