OSDN Git Service

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