OSDN Git Service

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