OSDN Git Service

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