OSDN Git Service

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