OSDN Git Service

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