OSDN Git Service

more rigorous SIGCHLD guarding
[pf3gnuchains/gcc-fork.git] / gcc / fixinc / fixincl.c
1 /* Install modified versions of certain ANSI-incompatible system header
2    files which are fixed to work correctly with ANSI C and placed in a
3    directory that GNU C will search.
4
5    Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
6
7 This file is part of GNU CC.
8
9 GNU CC is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 GNU CC is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with GNU CC; see the file COPYING.  If not, write to
21 the Free Software Foundation, 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.  */
23
24 #include "fixlib.h"
25
26 #if defined( HAVE_MMAP_FILE )
27 #include <sys/mman.h>
28 #define  BAD_ADDR ((void*)-1)
29 #endif
30
31 #include <signal.h>
32 #if ! defined( SIGCHLD ) && defined( SIGCLD )
33 #  define SIGCHLD SIGCLD
34 #endif
35 #ifndef SEPARATE_FIX_PROC
36 #include "server.h"
37 #endif
38
39 /*  The contents of this string are not very important.  It is mostly
40     just used as part of the "I am alive and working" test.  */
41
42 static const char program_id[] = "fixincl version 1.1";
43
44 /*  This format will be used at the start of every generated file */
45
46 static const char z_std_preamble[] =
47 "/*  DO NOT EDIT THIS FILE.\n\n\
48     It has been auto-edited by fixincludes from:\n\n\
49 \t\"%s/%s\"\n\n\
50     This had to be done to correct non-standard usages in the\n\
51     original, manufacturer supplied header file.  */\n\n";
52
53 /*  Working environment strings.  Essentially, invocation 'options'.  */
54
55 #define _ENV_(v,m,n,t)   tCC* v = NULL;
56 ENV_TABLE
57 #undef _ENV_
58
59 int find_base_len = 0;
60
61 typedef enum {
62   VERB_SILENT = 0,
63   VERB_FIXES,
64   VERB_APPLIES,
65   VERB_PROGRESS,
66   VERB_TESTS,
67   VERB_EVERYTHING
68 } te_verbose;
69
70 te_verbose  verbose_level = VERB_PROGRESS;
71 int have_tty = 0;
72
73 #define VLEVEL(l)  ((unsigned int) verbose_level >= (unsigned int) l)
74 #define NOT_SILENT VLEVEL(VERB_FIXES)
75
76 pid_t process_chain_head = (pid_t) -1;
77
78 char*  pz_curr_file;  /*  name of the current file under test/fix  */
79 char*  pz_curr_data;  /*  original contents of that file  */
80 char*  pz_temp_file;  /*  for DOS, a place to stash the temporary
81                           fixed data between system(3) calls  */
82 t_bool curr_data_mapped;
83 int    data_map_fd;
84 size_t data_map_size;
85 size_t ttl_data_size = 0;
86
87 #ifdef DO_STATS
88 int process_ct = 0;
89 int apply_ct = 0;
90 int fixed_ct = 0;
91 int altered_ct = 0;
92 #endif /* DO_STATS */
93
94 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
95 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
96 regex_t incl_quote_re;
97
98 static void do_version   PARAMS((void)) ATTRIBUTE_NORETURN;
99 char *load_file   PARAMS((const char *));
100 void run_compiles PARAMS((void));
101 void initialize   PARAMS((int argc,char** argv));
102 void process      PARAMS((void));
103
104 /*  External Source Code */
105
106 #include "fixincl.x"
107
108 /* * * * * * * * * * * * * * * * * * *
109  *
110  *  MAIN ROUTINE
111  */
112 extern int main PARAMS ((int, char **));
113 int
114 main (argc, argv)
115      int argc;
116      char **argv;
117 {
118   char *file_name_buf;
119
120   initialize ( argc, argv );
121
122   have_tty = isatty (fileno (stderr));
123
124   /* Before anything else, ensure we can allocate our file name buffer. */
125   file_name_buf = load_file_data (stdin);
126
127   /*  Because of the way server shells work, you have to keep stdin, out
128       and err open so that the proper input file does not get closed
129       by accident  */
130
131   freopen ("/dev/null", "r", stdin);
132
133   if (file_name_buf == (char *) NULL)
134     {
135       fputs ("No file names listed for fixing\n", stderr);
136       exit (EXIT_FAILURE);
137     }
138
139   for (;;)
140     {
141       char* pz_end;
142
143       /*  skip to start of name, past any "./" prefixes */
144
145       while (ISSPACE (*file_name_buf))  file_name_buf++;
146       while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
147         file_name_buf += 2;
148
149       /*  Check for end of list  */
150
151       if (*file_name_buf == NUL)
152         break;
153
154       /*  Set global file name pointer and find end of name */
155
156       pz_curr_file = file_name_buf;
157       pz_end = strchr( pz_curr_file, '\n' );
158       if (pz_end == (char*)NULL)
159         pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
160       else
161         file_name_buf = pz_end + 1;
162
163       while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1]))  pz_end--;
164
165       /*  IF no name is found (blank line) or comment marker, skip line  */
166
167       if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
168         continue;
169       *pz_end = NUL;
170
171       process ();
172     } /*  for (;;) */
173
174 #ifdef DO_STATS
175   if (VLEVEL( VERB_PROGRESS )) {
176     tSCC zFmt[] =
177       "\
178 Processed %5d files containing %d bytes    \n\
179 Applying  %5d fixes to %d files\n\
180 Altering  %5d of them\n";
181
182     fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
183              fixed_ct, altered_ct);
184   }
185 #endif /* DO_STATS */
186
187 # ifdef SEPARATE_FIX_PROC
188   unlink( pz_temp_file );
189 # endif
190   exit (EXIT_SUCCESS);
191 }
192
193
194 static void
195 do_version ()
196 {
197   static const char zFmt[] = "echo '%s'";
198   char zBuf[ 1024 ];
199
200   /* The 'version' option is really used to test that:
201      1.  The program loads correctly (no missing libraries)
202      2.  that we can compile all the regular expressions.
203      3.  we can correctly run our server shell process
204   */
205   run_compiles ();
206   sprintf (zBuf, zFmt, program_id);
207 #ifndef SEPARATE_FIX_PROC
208   puts (zBuf + 5);
209   exit (strcmp (run_shell (zBuf), program_id));
210 #else
211   exit (system (zBuf));
212 #endif
213 }
214
215 /* * * * * * * * * * * * */
216
217 void
218 initialize ( argc, argv )
219   int argc;
220   char** argv;
221 {
222   static const char var_not_found[] =
223     "fixincl ERROR:  %s environment variable not defined\n"
224 #ifdef __STDC__
225     "each of these must be defined:\n"
226 #define _ENV_(v,m,n,t) "\t" n "  - " t "\n"
227 ENV_TABLE
228 #undef _ENV_
229 #endif
230     ;
231
232   xmalloc_set_program_name (argv[0]);
233
234   switch (argc)
235     {
236     case 1:
237       break;
238
239     case 2:
240       if (strcmp (argv[1], "-v") == 0)
241         do_version ();
242       if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
243         {
244           fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
245                    errno, xstrerror (errno), argv[1] );
246           exit (EXIT_FAILURE);
247         }
248       break;
249
250     default:
251       fputs ("fixincl ERROR:  too many command line arguments\n", stderr);
252       exit (EXIT_FAILURE);
253     }
254
255 #ifdef SIGCHLD
256   /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
257      receive the signal.  A different setting is inheritable */
258   signal (SIGCHLD, SIG_DFL);
259 #endif
260
261 #define _ENV_(v,m,n,t)   { tSCC var[] = n;  \
262   v = getenv (var); if (m && (v == NULL)) { \
263   fprintf (stderr, var_not_found, var);     \
264   exit (EXIT_FAILURE); } }
265
266 ENV_TABLE
267
268 #undef _ENV_
269
270   if (ISDIGIT ( *pz_verbose ))
271     verbose_level = (te_verbose)atoi( pz_verbose );
272   else
273     switch (*pz_verbose) {
274     case 's':
275     case 'S':
276       verbose_level = VERB_SILENT;     break;
277
278     case 'f':
279     case 'F':
280       verbose_level = VERB_FIXES;      break;
281
282     case 'a':
283     case 'A':
284       verbose_level = VERB_APPLIES;    break;
285
286     case 'p':
287     case 'P':
288       verbose_level = VERB_PROGRESS;   break;
289
290     case 't':
291     case 'T':
292       verbose_level = VERB_TESTS;      break;
293
294     case 'e':
295     case 'E':
296       verbose_level = VERB_EVERYTHING; break;
297     }
298
299  while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
300    pz_find_base += 2;
301  if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
302    find_base_len = strlen( pz_find_base );
303
304   /*  Compile all the regular expressions now.
305       That way, it is done only once for the whole run.
306       */
307   run_compiles ();
308
309 # ifdef SEPARATE_FIX_PROC
310   /* NULL as the first argument to `tempnam' causes it to DTRT
311      wrt the temporary directory where the file will be created.  */
312   pz_temp_file = tempnam( NULL, "fxinc" );
313 # endif
314
315   signal (SIGQUIT, SIG_IGN);
316 #ifdef SIGIOT
317   signal (SIGIOT,  SIG_IGN);
318 #endif
319 #ifdef SIGPIPE
320   signal (SIGPIPE, SIG_IGN);
321 #endif
322   signal (SIGALRM, SIG_IGN);
323   signal (SIGTERM, SIG_IGN);
324 }
325
326 /* * * * * * * * * * * * *
327
328    load_file loads all the contents of a file into malloc-ed memory.
329    Its argument is the name of the file to read in; the returned
330    result is the NUL terminated contents of the file.  The file
331    is presumed to be an ASCII text file containing no NULs.  */
332 char *
333 load_file ( fname )
334     const char* fname;
335 {
336   struct stat stbf;
337   char* res;
338
339   if (stat (fname, &stbf) != 0)
340     {
341       if (NOT_SILENT)
342         fprintf (stderr, "error %d (%s) stat-ing %s\n",
343                  errno, xstrerror (errno), fname );
344       return (char *) NULL;
345     }
346   if (stbf.st_size == 0)
347     return (char*)NULL;
348
349   /*  Make the data map size one larger than the file size for documentation
350       purposes.  Truth is that there will be a following NUL character if
351       the file size is not a multiple of the page size.  If it is a multiple,
352       then this adjustment sometimes fails anyway.  */
353   data_map_size = stbf.st_size+1;
354   data_map_fd   = open (fname, O_RDONLY);
355   ttl_data_size += data_map_size-1;
356
357   if (data_map_fd < 0)
358     {
359       if (NOT_SILENT)
360         fprintf (stderr, "error %d (%s) opening %s for read\n",
361                  errno, xstrerror (errno), fname);
362       return (char*)NULL;
363     }
364
365 #ifdef HAVE_MMAP_FILE
366   curr_data_mapped = BOOL_TRUE;
367
368   /*  IF the file size is a multiple of the page size,
369       THEN sometimes you will seg fault trying to access a trailing byte */
370   if ((stbf.st_size & (getpagesize()-1)) == 0)
371     res = (char*)BAD_ADDR;
372   else
373     res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
374                        MAP_PRIVATE, data_map_fd, 0);
375   if (res == (char*)BAD_ADDR)
376 #endif
377     {
378       FILE* fp = fdopen (data_map_fd, "r");
379       curr_data_mapped = BOOL_FALSE;
380       res = load_file_data (fp);
381       fclose (fp);
382     }
383
384   return res;
385 }
386
387 static int machine_matches PARAMS ((tFixDesc *));
388 static int
389 machine_matches( p_fixd )
390   tFixDesc *p_fixd;
391         {
392 # ifndef SEPARATE_FIX_PROC
393           tSCC case_fmt[] = "case %s in\n";     /*  9 bytes, plus string */
394           tSCC esac_fmt[] =
395                " )\n    echo %s ;;\n* ) echo %s ;;\nesac";/*  4 bytes */
396           tSCC skip[] = "skip";                 /*  4 bytes */
397           tSCC run[] = "run";                   /*  3 bytes */
398           /* total bytes to add to machine sum:    49 - see fixincl.tpl */
399
400           const char **papz_machs = p_fixd->papz_machs;
401           char *pz;
402           const char *pz_sep = "";
403           tCC *pz_if_true;
404           tCC *pz_if_false;
405           char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
406
407           /* Start the case statement */
408
409           sprintf (cmd_buf, case_fmt, pz_machine);
410           pz = cmd_buf + strlen (cmd_buf);
411
412           /*  Determine if a match means to apply the fix or not apply it */
413
414           if (p_fixd->fd_flags & FD_MACH_IFNOT)
415             {
416               pz_if_true  = skip;
417               pz_if_false = run;
418             }
419           else
420             {
421               pz_if_true  = run;
422               pz_if_false = skip;
423             }
424
425           /*  Emit all the machine names.  If there are more than one,
426               then we will insert " | \\\n" between the names  */
427
428           for (;;)
429             {
430               const char* pz_mach = *(papz_machs++);
431
432               if (pz_mach == (const char*) NULL)
433                 break;
434               sprintf (pz, "%s%s", pz_sep, pz_mach);
435               pz += strlen (pz);
436               pz_sep = " | \\\n";
437             }
438
439           /* Now emit the match and not-match actions and the esac */
440
441           sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
442
443           /*  Run the script.
444               The result will start either with 's' or 'r'.  */
445
446           {
447             int skip;
448             pz = run_shell (cmd_buf);
449             skip = (*pz == 's');
450             free ( (void*)pz );
451             if (skip)
452               {
453                 p_fixd->fd_flags |= FD_SKIP_TEST;
454                 return BOOL_FALSE;
455               }
456           }
457
458   return BOOL_TRUE;
459 # else /* is SEPARATE_FIX_PROC */
460   const char **papz_machs = p_fixd->papz_machs;
461   int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
462   for (;;)
463     {
464       const char* pz_mach = *(papz_machs++);
465
466       if (pz_mach == (const char*) NULL)
467         break;
468       if (strstr (pz_mach, "dos") != NULL && !invert)
469         return BOOL_TRUE;
470     }
471
472   p_fixd->fd_flags |= FD_SKIP_TEST;
473   return BOOL_FALSE;
474 # endif
475 }
476
477 /* * * * * * * * * * * * *
478
479    run_compiles   run all the regexp compiles for all the fixes once.
480    */
481 void
482 run_compiles ()
483 {
484   tFixDesc *p_fixd = fixDescList;
485   int fix_ct = FIX_COUNT;
486   regex_t *p_re = (regex_t *) xmalloc (REGEX_COUNT * sizeof (regex_t));
487
488   /*  Make sure compile_re does not stumble across invalid data */
489
490   memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
491   memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
492
493   compile_re (incl_quote_pat, &incl_quote_re, 1,
494               "quoted include", "run_compiles");
495
496   /*  Allow machine name tests to be ignored (testing, mainly) */
497
498   if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
499     pz_machine = (char*)NULL;
500
501   /* FOR every fixup, ...  */
502   do
503     {
504       tTestDesc *p_test = p_fixd->p_test_desc;
505       int test_ct = p_fixd->test_ct;
506
507       /*  IF the machine type pointer is not NULL (we are not in test mode)
508              AND this test is for or not done on particular machines
509           THEN ...   */
510
511       if (  (pz_machine != NULL)
512          && (p_fixd->papz_machs != (const char**) NULL)
513          && ! machine_matches (p_fixd) )
514         continue;
515
516       /* FOR every test for the fixup, ...  */
517
518       while (--test_ct >= 0)
519         {
520           switch (p_test->type)
521             {
522             case TT_EGREP:
523             case TT_NEGREP:
524               p_test->p_test_regex = p_re++;
525               compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
526                           "select test", p_fixd->fix_name);
527             default: break;
528             }
529           p_test++;
530         }
531     }
532   while (p_fixd++, --fix_ct > 0);
533 }
534
535
536 /* * * * * * * * * * * * *
537
538    create_file  Create the output modified file.
539    Input:    the name of the file to create
540    Returns:  a file pointer to the new, open file  */
541
542 #if defined(S_IRUSR) && defined(S_IWUSR) && \
543     defined(S_IRGRP) && defined(S_IROTH)
544
545 #   define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
546 #else
547 #   define S_IRALL 0644
548 #endif
549
550 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
551     defined(S_IROTH) && defined(S_IXOTH)
552
553 #   define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
554 #else
555 #   define S_DIRALL 0755
556 #endif
557
558
559 static FILE *create_file PARAMS ((void));
560 static FILE *
561 create_file ()
562 {
563   int fd;
564   FILE *pf;
565   char fname[MAXPATHLEN];
566
567   sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
568
569   fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
570
571   /*  We may need to create the directories needed... */
572   if ((fd < 0) && (errno == ENOENT))
573     {
574       char *pz_dir = strchr (fname + 1, '/');
575       struct stat stbf;
576
577       while (pz_dir != (char *) NULL)
578         {
579           *pz_dir = NUL;
580           if (stat (fname, &stbf) < 0)
581             {
582               mkdir (fname, S_IFDIR | S_DIRALL);
583             }
584
585           *pz_dir = '/';
586           pz_dir = strchr (pz_dir + 1, '/');
587         }
588
589       /*  Now, lets try the open again... */
590       fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
591     }
592   if (fd < 0)
593     {
594       fprintf (stderr, "Error %d (%s) creating %s\n",
595                errno, xstrerror (errno), fname);
596       exit (EXIT_FAILURE);
597     }
598   if (NOT_SILENT)
599     fprintf (stderr, "Fixed:  %s\n", pz_curr_file);
600   pf = fdopen (fd, "w");
601
602   /*
603    *  IF pz_machine is NULL, then we are in some sort of test mode.
604    *  Do not insert the current directory name.  Use a constant string.
605    */
606   fprintf (pf, z_std_preamble,
607            (pz_machine == NULL)
608            ? "fixinc/tests/inc"
609            : pz_input_dir,
610            pz_curr_file);
611
612   return pf;
613 }
614
615
616 /* * * * * * * * * * * * *
617
618   test_test   make sure a shell-style test expression passes.
619   Input:  a pointer to the descriptor of the test to run and
620           the name of the file that we might want to fix
621   Result: APPLY_FIX or SKIP_FIX, depending on the result of the
622           shell script we run.  */
623 #ifndef SEPARATE_FIX_PROC
624 static int test_test PARAMS ((tTestDesc *, char *));
625 static int
626 test_test (p_test, pz_test_file)
627      tTestDesc *p_test;
628      char*      pz_test_file;
629 {
630   tSCC cmd_fmt[] =
631 "file=%s\n\
632 if ( test %s ) > /dev/null 2>&1\n\
633 then echo TRUE\n\
634 else echo FALSE\n\
635 fi";
636
637   char *pz_res;
638   int res;
639
640   static char cmd_buf[4096];
641
642   sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
643   pz_res = run_shell (cmd_buf);
644
645   switch (*pz_res) {
646   case 'T':
647     res = APPLY_FIX;
648     break;
649
650   case 'F':
651     res = SKIP_FIX;
652     break;
653
654   default:
655     fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
656              pz_res, cmd_buf );
657   }
658
659   free ((void *) pz_res);
660   return res;
661 }
662 #else
663 /*
664  *  IF we are in MS-DOS land, then whatever shell-type test is required
665  *  will, by definition, fail
666  */
667 #define test_test(t,tf)  SKIP_FIX
668 #endif
669
670 /* * * * * * * * * * * * *
671
672   egrep_test   make sure an egrep expression is found in the file text.
673   Input:  a pointer to the descriptor of the test to run and
674           the pointer to the contents of the file under suspicion
675   Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
676
677   The caller may choose to reverse meaning if the sense of the test
678   is inverted.  */
679
680 static int egrep_test PARAMS ((char *, tTestDesc *));
681 static int
682 egrep_test (pz_data, p_test)
683      char *pz_data;
684      tTestDesc *p_test;
685 {
686 #ifdef DEBUG
687   if (p_test->p_test_regex == 0)
688     fprintf (stderr, "fixincl ERROR RE not compiled:  `%s'\n",
689              p_test->pz_test_text);
690 #endif
691   if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
692     return APPLY_FIX;
693   return SKIP_FIX;
694 }
695
696
697 /* * * * * * * * * * * * *
698
699   quoted_file_exists  Make sure that a file exists before we emit
700   the file name.  If we emit the name, our invoking shell will try
701   to copy a non-existing file into the destination directory.  */
702
703 static int quoted_file_exists PARAMS ((const char *, const char *, const char *));
704 static int
705 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
706      const char *pz_src_path;
707      const char *pz_file_path;
708      const char *pz_file;
709 {
710   char z[ MAXPATHLEN ];
711   char* pz;
712   sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
713   pz = z + strlen ( z );
714
715   for (;;) {
716     char ch = *pz_file++;
717     if (! ISGRAPH( ch ))
718       return 0;
719     if (ch == '"')
720       break;
721     *pz++ = ch;
722   }
723   *pz = '\0';
724   {
725     struct stat s;
726     if (stat (z, &s) != 0)
727       return 0;
728     return S_ISREG( s.st_mode );
729   }
730 }
731
732
733 /* * * * * * * * * * * * *
734  *
735    extract_quoted_files
736
737    The syntax, `#include "file.h"' specifies that the compiler is to
738    search the local directory of the current file before the include
739    list.  Consequently, if we have modified a header and stored it in
740    another directory, any files that are included by that modified
741    file in that fashion must also be copied into this new directory.
742    This routine finds those flavors of #include and for each one found
743    emits a triple of:
744
745     1.  source directory of the original file
746     2.  the relative path file name of the #includ-ed file
747     3.  the full destination path for this file
748
749    Input:  the text of the file, the file name and a pointer to the
750            match list where the match information was stored.
751    Result: internally nothing.  The results are written to stdout
752            for interpretation by the invoking shell  */
753
754
755 static void extract_quoted_files PARAMS ((char *, const char *, regmatch_t *));
756 static void
757 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
758      char *pz_data;
759      const char *pz_fixed_file;
760      regmatch_t *p_re_match;
761 {
762   char *pz_dir_end = strrchr (pz_fixed_file, '/');
763   char *pz_incl_quot = pz_data;
764
765   if (VLEVEL( VERB_APPLIES ))
766     fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
767
768   /*  Set "pz_fixed_file" to point to the containing subdirectory of the source
769       If there is none, then it is in our current directory, ".".   */
770
771   if (pz_dir_end == (char *) NULL)
772     pz_fixed_file = ".";
773   else
774     *pz_dir_end = '\0';
775
776   for (;;)
777     {
778       pz_incl_quot += p_re_match->rm_so;
779
780       /*  Skip forward to the included file name */
781       while (*pz_incl_quot != '"')
782         pz_incl_quot++;
783
784       if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
785         {
786           /* Print the source directory and the subdirectory
787              of the file in question.  */
788           printf ("%s  %s/", pz_src_dir, pz_fixed_file);
789           pz_dir_end = pz_incl_quot;
790
791           /* Append to the directory the relative path of the desired file */
792           while (*pz_incl_quot != '"')
793             putc (*pz_incl_quot++, stdout);
794
795           /* Now print the destination directory appended with the
796              relative path of the desired file */
797           printf ("  %s/%s/", pz_dest_dir, pz_fixed_file);
798           while (*pz_dir_end != '"')
799             putc (*pz_dir_end++, stdout);
800
801           /* End of entry */
802           putc ('\n', stdout);
803         }
804
805       /* Find the next entry */
806       if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
807         break;
808     }
809 }
810
811
812 /* * * * * * * * * * * * *
813
814     Somebody wrote a *_fix subroutine that we must call.
815     */
816 #ifndef SEPARATE_FIX_PROC
817 static int internal_fix PARAMS ((int, tFixDesc *));
818 static int
819 internal_fix (read_fd, p_fixd)
820   int read_fd;
821   tFixDesc* p_fixd;
822 {
823   int fd[2];
824
825   if (pipe( fd ) != 0)
826     {
827       fprintf (stderr, "Error %d on pipe(2) call\n", errno );
828       exit (EXIT_FAILURE);
829     }
830
831   for (;;)
832     {
833       pid_t childid = fork();
834
835       switch (childid)
836         {
837         case -1:
838           break;
839
840         case 0:
841           close (fd[0]);
842           goto do_child_task;
843
844         default:
845           /*
846            *  Parent process
847            */
848           close (read_fd);
849           close (fd[1]);
850           return fd[0];
851         }
852
853       /*
854        *  Parent in error
855        */
856       fprintf (stderr, z_fork_err, errno, xstrerror (errno),
857                p_fixd->fix_name);
858       {
859         static int failCt = 0;
860         if ((errno != EAGAIN) || (++failCt > 10))
861           exit (EXIT_FAILURE);
862         sleep (1);
863       }
864     } do_child_task:;
865
866   /*
867    *  Close our current stdin and stdout
868    */
869   close (STDIN_FILENO);
870   close (STDOUT_FILENO);
871   UNLOAD_DATA();
872
873   /*
874    *  Make the fd passed in the stdin, and the write end of
875    *  the new pipe become the stdout.
876    */
877   fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
878   fcntl (read_fd, F_DUPFD, STDIN_FILENO);
879
880   apply_fix (p_fixd, pz_curr_file);
881   exit (0);
882 }
883 #endif /* !SEPARATE_FIX_PROC */
884
885
886 #ifdef SEPARATE_FIX_PROC
887 static void
888 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
889   tFixDesc* p_fixd;
890   tCC* pz_fix_file;
891   tCC* pz_file_source;
892   tCC* pz_temp_file;
893 {
894   char*  pz_cmd;
895   char*  pz_scan;
896   size_t argsize;
897
898   if (p_fixd->fd_flags & FD_SUBROUTINE)
899     {
900       tSCC z_applyfix_prog[] = "/fixinc/applyfix";
901
902       argsize = 32
903               + strlen( pz_orig_dir )
904               + sizeof( z_applyfix_prog )
905               + strlen( pz_fix_file )
906               + strlen( pz_file_source )
907               + strlen( pz_temp_file );
908
909       pz_cmd = (char*)xmalloc( argsize );
910
911       strcpy( pz_cmd, pz_orig_dir );
912       pz_scan = pz_cmd + strlen( pz_orig_dir );
913       strcpy( pz_scan, z_applyfix_prog );
914       pz_scan += sizeof( z_applyfix_prog ) - 1;
915       *(pz_scan++) = ' ';
916
917       /*
918        *  Now add the fix number and file names that may be needed
919        */
920       sprintf (pz_scan, "%ld %s %s %s", p_fixd - fixDescList,
921                pz_fix_file, pz_file_source, pz_temp_file);
922     }
923   else /* NOT an "internal" fix: */
924     {
925       size_t parg_size;
926 #ifdef __MSDOS__
927       /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
928          dst is a temporary file anyway, so we know there's no other
929          file by that name; and DOS's system(3) doesn't mind to
930          clobber existing file in redirection.  Besides, with DOS 8+3
931          limited file namespace, we can easily lose if dst already has
932          an extension that is 3 or more characters long.
933
934          I do not think the 8+3 issue is relevant because all the files
935          we operate on are named "*.h", making 8+2 adequate.  Anyway,
936          the following bizarre use of 'cat' only works on DOS boxes.
937          It causes the file to be dropped into a temporary file for
938          'cat' to read (pipes do not work on DOS).  */
939       tSCC   z_cmd_fmt[] = " %s | cat > %s";
940 #else
941       /* Don't use positional formatting arguments because some lame-o
942          implementations cannot cope  :-(.  */
943       tSCC   z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
944 #endif
945       tCC**  ppArgs = p_fixd->patch_args;
946
947       argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
948               + strlen( pz_file_source );
949       parg_size = argsize;
950       
951
952       /*
953        *  Compute the size of the command line.  Add lotsa extra space
954        *  because some of the args to sed use lotsa single quotes.
955        *  (This requires three extra bytes per quote.  Here we allow
956        *  for up to 8 single quotes for each argument, including the
957        *  command name "sed" itself.  Nobody will *ever* need more. :)
958        */
959       for (;;)
960         {
961           tCC* p_arg = *(ppArgs++);
962           if (p_arg == NULL)
963             break;
964           argsize += 24 + strlen( p_arg );
965         }
966
967       /* Estimated buffer size we will need.  */
968       pz_scan = pz_cmd = (char*)xmalloc( argsize );
969       /* How much of it do we allot to the program name and its
970          arguments.  */
971       parg_size = argsize - parg_size;
972
973       ppArgs = p_fixd->patch_args;
974
975       /*
976        *  Copy the program name, unquoted
977        */
978       {
979         tCC*   pArg = *(ppArgs++);
980         for (;;)
981           {
982             char ch = *(pArg++);
983             if (ch == NUL)
984               break;
985             *(pz_scan++) = ch;
986           }
987       }
988
989       /*
990        *  Copy the program arguments, quoted
991        */
992       for (;;)
993         {
994           tCC*   pArg = *(ppArgs++);
995           char*  pz_scan_save;
996           if (pArg == NULL)
997             break;
998           *(pz_scan++) = ' ';
999           pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
1000                                         parg_size - (pz_scan - pz_cmd) );
1001           /*
1002            *  Make sure we don't overflow the buffer due to sloppy
1003            *  size estimation.
1004            */
1005           while (pz_scan == (char*)NULL)
1006             {
1007               size_t already_filled = pz_scan_save - pz_cmd;
1008               pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
1009               pz_scan_save = pz_scan = pz_cmd + already_filled;
1010               parg_size += 100;
1011               pz_scan = make_raw_shell_str( pz_scan, pArg,
1012                                             parg_size - (pz_scan - pz_cmd) );
1013             }
1014         }
1015
1016       /*
1017        *  add the file machinations.
1018        */
1019 #ifdef __MSDOS__
1020       sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1021 #else
1022       sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1023                pz_temp_file, pz_temp_file, pz_temp_file);
1024 #endif
1025     }
1026   system( pz_cmd );
1027   free( (void*)pz_cmd );
1028 }
1029
1030 /* * * * * * * * * * * * *
1031
1032     This loop should only cycle for 1/2 of one loop.
1033     "chain_open" starts a process that uses "read_fd" as
1034     its stdin and returns the new fd this process will use
1035     for stdout.  */
1036
1037 #else /* is *NOT* SEPARATE_FIX_PROC */
1038 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1039 static int
1040 start_fixer (read_fd, p_fixd, pz_fix_file)
1041   int read_fd;
1042   tFixDesc* p_fixd;
1043   char* pz_fix_file;
1044 {
1045   tCC* pz_cmd_save;
1046   char* pz_cmd;
1047
1048   if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1049     return internal_fix (read_fd, p_fixd);
1050
1051   if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1052     pz_cmd = (char*)NULL;
1053   else
1054     {
1055       tSCC z_cmd_fmt[] = "file='%s'\n%s";
1056       pz_cmd = (char*) xmalloc (strlen (p_fixd->patch_args[2])
1057                                 + sizeof( z_cmd_fmt )
1058                                 + strlen( pz_fix_file ));
1059       sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1060       pz_cmd_save = p_fixd->patch_args[2];
1061       p_fixd->patch_args[2] = pz_cmd;
1062     }
1063
1064   /*  Start a fix process, handing off the  previous read fd for its
1065       stdin and getting a new fd that reads from the fix process' stdout.
1066       We normally will not loop, but we will up to 10 times if we keep
1067       getting "EAGAIN" errors.
1068
1069       */
1070   for (;;)
1071     {
1072       static int failCt = 0;
1073       int fd;
1074
1075       fd = chain_open (read_fd,
1076                        (tCC **) p_fixd->patch_args,
1077                        (process_chain_head == -1)
1078                        ? &process_chain_head : (pid_t *) NULL);
1079
1080       if (fd != -1)
1081         {
1082           read_fd = fd;
1083           break;
1084         }
1085
1086       fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1087                p_fixd->fix_name);
1088
1089       if ((errno != EAGAIN) || (++failCt > 10))
1090         exit (EXIT_FAILURE);
1091       sleep (1);
1092     }
1093
1094   /*  IF we allocated a shell script command,
1095       THEN free it and restore the command format to the fix description */
1096   if (pz_cmd != (char*)NULL)
1097     {
1098       free ((void*)pz_cmd);
1099       p_fixd->patch_args[2] = pz_cmd_save;
1100     }
1101
1102   return read_fd;
1103 }
1104 #endif
1105
1106
1107 /* * * * * * * * * * * * *
1108
1109    Process the potential fixes for a particular include file.
1110    Input:  the original text of the file and the file's name
1111    Result: none.  A new file may or may not be created.  */
1112
1113 static t_bool fix_applies PARAMS ((tFixDesc *));
1114 static t_bool
1115 fix_applies (p_fixd)
1116   tFixDesc *p_fixd;
1117 {
1118   const char *pz_fname = pz_curr_file;
1119   const char *pz_scan = p_fixd->file_list;
1120   int test_ct;
1121   tTestDesc *p_test;
1122
1123 # ifdef SEPARATE_FIX_PROC
1124   /*
1125    *  There is only one fix that uses a shell script as of this writing.
1126    *  I hope to nuke it anyway, it does not apply to DOS and it would
1127    *  be painful to implement.  Therefore, no "shell" fixes for DOS.
1128    */
1129   if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1130     return BOOL_FALSE;
1131 # else
1132   if (p_fixd->fd_flags & FD_SKIP_TEST)
1133     return BOOL_FALSE;
1134 # endif
1135
1136   /*  IF there is a file name restriction,
1137       THEN ensure the current file name matches one in the pattern  */
1138
1139   if (pz_scan != (char *) NULL)
1140     {
1141       size_t name_len;
1142
1143       while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1144         pz_fname += 2;
1145       name_len = strlen (pz_fname);
1146
1147       for (;;)
1148         {
1149           pz_scan = strstr (pz_scan + 1, pz_fname);
1150           /*  IF we can't match the string at all,
1151               THEN bail  */
1152           if (pz_scan == (char *) NULL)
1153             return BOOL_FALSE;
1154
1155           /*  IF the match is surrounded by the '|' markers,
1156               THEN we found a full match -- time to run the tests  */
1157
1158           if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1159             break;
1160         }
1161     }
1162
1163   /*  FOR each test, see if it fails.
1164       IF it does fail, then we go on to the next test */
1165
1166   for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1167        test_ct-- > 0;
1168        p_test++)
1169     {
1170       switch (p_test->type)
1171         {
1172         case TT_TEST:
1173           if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1174 #ifdef DEBUG
1175             if (VLEVEL( VERB_EVERYTHING ))
1176               fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1177                        pz_fname, p_fixd->test_ct - test_ct);
1178 #endif
1179             return BOOL_FALSE;
1180           }
1181           break;
1182
1183         case TT_EGREP:
1184           if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1185 #ifdef DEBUG
1186             if (VLEVEL( VERB_EVERYTHING ))
1187               fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1188                        pz_fname, p_fixd->test_ct - test_ct);
1189 #endif
1190             return BOOL_FALSE;
1191           }
1192           break;
1193
1194         case TT_NEGREP:
1195           if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1196 #ifdef DEBUG
1197             if (VLEVEL( VERB_EVERYTHING ))
1198               fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1199                        pz_fname, p_fixd->test_ct - test_ct);
1200 #endif
1201             /*  Negated sense  */
1202             return BOOL_FALSE;
1203           }
1204           break;
1205
1206         case TT_FUNCTION:
1207           if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1208               != APPLY_FIX) {
1209 #ifdef DEBUG
1210             if (VLEVEL( VERB_EVERYTHING ))
1211               fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1212                        pz_fname, p_fixd->test_ct - test_ct);
1213 #endif
1214             return BOOL_FALSE;
1215           }
1216           break;
1217         }
1218     }
1219
1220   return BOOL_TRUE;
1221 }
1222
1223
1224 /* * * * * * * * * * * * *
1225
1226    Write out a replacement file  */
1227
1228 static void write_replacement PARAMS ((tFixDesc *));
1229 static void
1230 write_replacement (p_fixd)
1231   tFixDesc *p_fixd;
1232 {
1233    const char* pz_text = p_fixd->patch_args[0];
1234
1235    if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1236      return;
1237
1238    {
1239      FILE* out_fp = create_file ();
1240      fputs (pz_text, out_fp);
1241      fclose (out_fp);
1242    }
1243 }
1244
1245
1246 /* * * * * * * * * * * * *
1247
1248     We have work to do.  Read back in the output
1249     of the filtering chain.  Compare each byte as we read it with
1250     the contents of the original file.  As soon as we find any
1251     difference, we will create the output file, write out all
1252     the matched text and then copy any remaining data from the
1253     output of the filter chain.
1254     */
1255 static void test_for_changes PARAMS ((int));
1256 static void
1257 test_for_changes (read_fd)
1258   int read_fd;
1259 {
1260   FILE *in_fp = fdopen (read_fd, "r");
1261   FILE *out_fp = (FILE *) NULL;
1262   char *pz_cmp = pz_curr_data;
1263
1264 #ifdef DO_STATS
1265   fixed_ct++;
1266 #endif
1267   for (;;)
1268     {
1269       int ch;
1270
1271       ch = getc (in_fp);
1272       if (ch == EOF)
1273         break;
1274
1275       /*  IF we are emitting the output
1276           THEN emit this character, too.
1277       */
1278       if (out_fp != (FILE *) NULL)
1279         putc (ch, out_fp);
1280
1281       /*  ELSE if this character does not match the original,
1282           THEN now is the time to start the output.
1283       */
1284       else if (ch != *pz_cmp)
1285         {
1286           out_fp = create_file ();
1287
1288 #ifdef DO_STATS
1289           altered_ct++;
1290 #endif
1291           /*  IF there are matched data, write the matched part now. */
1292           if (pz_cmp != pz_curr_data)
1293             fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
1294
1295           /*  Emit the current unmatching character */
1296           putc (ch, out_fp);
1297         }
1298       else
1299         /*  ELSE the character matches.  Advance the compare ptr */
1300         pz_cmp++;
1301     }
1302
1303   /*  IF we created the output file, ... */
1304   if (out_fp != (FILE *) NULL)
1305     {
1306       regmatch_t match;
1307
1308       /* Close the file and see if we have to worry about
1309          `#include "file.h"' constructs.  */
1310       fclose (out_fp);
1311       if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1312         extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1313     }
1314
1315   fclose (in_fp);
1316   close (read_fd);  /* probably redundant, but I'm paranoid */
1317 }
1318
1319
1320 /* * * * * * * * * * * * *
1321
1322    Process the potential fixes for a particular include file.
1323    Input:  the original text of the file and the file's name
1324    Result: none.  A new file may or may not be created.  */
1325
1326 void
1327 process ()
1328 {
1329   tFixDesc *p_fixd = fixDescList;
1330   int todo_ct = FIX_COUNT;
1331   int read_fd = -1;
1332 # ifndef SEPARATE_FIX_PROC
1333   int num_children = 0;
1334 # else /* is SEPARATE_FIX_PROC */
1335   char* pz_file_source = pz_curr_file;
1336 # endif
1337
1338   if (access (pz_curr_file, R_OK) != 0)
1339     {
1340       int erno = errno;
1341       fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1342                pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1343                erno, xstrerror (erno));
1344       return;
1345     }
1346
1347   pz_curr_data = load_file (pz_curr_file);
1348   if (pz_curr_data == (char *) NULL)
1349     return;
1350
1351 #ifdef DO_STATS
1352   process_ct++;
1353 #endif
1354   if (VLEVEL( VERB_PROGRESS ) && have_tty)
1355     fprintf (stderr, "%6d %-50s   \r", data_map_size, pz_curr_file );
1356
1357 # ifndef SEPARATE_FIX_PROC
1358   process_chain_head = NOPROCESS;
1359
1360   /* For every fix in our fix list, ...  */
1361   for (; todo_ct > 0; p_fixd++, todo_ct--)
1362     {
1363       if (! fix_applies (p_fixd))
1364         continue;
1365
1366       if (VLEVEL( VERB_APPLIES ))
1367         fprintf (stderr, "Applying %-24s to %s\n",
1368                  p_fixd->fix_name, pz_curr_file);
1369
1370       if (p_fixd->fd_flags & FD_REPLACEMENT)
1371         {
1372           write_replacement (p_fixd);
1373           UNLOAD_DATA();
1374           return;
1375         }
1376
1377       /*  IF we do not have a read pointer,
1378           THEN this is the first fix for the current file.
1379           Open the source file.  That will be used as stdin for
1380           the first fix.  Any subsequent fixes will use the
1381           stdout descriptor of the previous fix for its stdin.  */
1382
1383       if (read_fd == -1)
1384         {
1385           read_fd = open (pz_curr_file, O_RDONLY);
1386           if (read_fd < 0)
1387             {
1388               fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1389                        xstrerror (errno), pz_curr_file);
1390               exit (EXIT_FAILURE);
1391             }
1392
1393           /*  Ensure we do not get duplicate output */
1394
1395           fflush (stdout);
1396         }
1397
1398       read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1399       num_children++;
1400     }
1401
1402   /*  IF we have a read-back file descriptor,
1403       THEN check for changes and write output if changed.   */
1404
1405   if (read_fd >= 0)
1406     {
1407       test_for_changes (read_fd);
1408 #ifdef DO_STATS
1409       apply_ct += num_children;
1410 #endif
1411       /* Wait for child processes created by chain_open()
1412          to avoid leaving zombies.  */
1413       do  {
1414         wait ((int *) NULL);
1415       } while (--num_children > 0);
1416     }
1417
1418 # else /* is SEPARATE_FIX_PROC */
1419
1420   for (; todo_ct > 0; p_fixd++, todo_ct--)
1421     {
1422       if (! fix_applies (p_fixd))
1423         continue;
1424
1425       if (VLEVEL( VERB_APPLIES ))
1426         fprintf (stderr, "Applying %-24s to %s\n",
1427                  p_fixd->fix_name, pz_curr_file);
1428
1429       if (p_fixd->fd_flags & FD_REPLACEMENT)
1430         {
1431           write_replacement (p_fixd);
1432           UNLOAD_DATA();
1433           return;
1434         }
1435       fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1436       pz_file_source = pz_temp_file;
1437     }
1438
1439   read_fd = open (pz_temp_file, O_RDONLY);
1440   if (read_fd < 0)
1441     {
1442       if (errno != ENOENT)
1443         fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1444                  errno, xstrerror (errno), pz_temp_file);
1445     }
1446   else
1447     {
1448       test_for_changes (read_fd);
1449       /* Unlinking a file while it is still open is a Bad Idea on
1450          DOS/Windows.  */
1451       close (read_fd);
1452       unlink (pz_temp_file);
1453     }
1454
1455 # endif
1456   UNLOAD_DATA();
1457 }