OSDN Git Service

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