OSDN Git Service

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