OSDN Git Service

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