OSDN Git Service

Implement, but leave disabled, MSDOS functionality
[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       tSCC   z_cmd_fmt[] = " %s > %s";
921       tCC**  ppArgs = p_fixd->patch_args;
922
923       argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
924               + strlen( pz_file_source );
925       parg_size = argsize;
926       
927
928       /*
929        *  Compute the size of the command line.  Add lotsa extra space
930        *  because some of the args to sed use lotsa single quotes.
931        *  (This requires three extra bytes per quote.  Here we allow
932        *  for up to 8 single quotes for each argument, including the
933        *  command name "sed" itself.  Nobody will *ever* need more. :)
934        */
935       for (;;)
936         {
937           tCC* p_arg = *(ppArgs++);
938           if (p_arg == NULL)
939             break;
940           argsize += 24 + strlen( p_arg );
941         }
942
943       /* Estimated buffer size we will need.  */
944       pz_scan = pz_cmd = (char*)xmalloc( argsize );
945       /* How much of it do we allot to the program name and its
946          arguments.  */
947       parg_size = argsize - parg_size;
948
949       ppArgs = p_fixd->patch_args;
950
951       /*
952        *  Copy the program name, unquoted
953        */
954       {
955         tCC*   pArg = *(ppArgs++);
956         for (;;)
957           {
958             char ch = *(pArg++);
959             if (ch == NUL)
960               break;
961             *(pz_scan++) = ch;
962           }
963       }
964
965       /*
966        *  Copy the program arguments, quoted
967        */
968       for (;;)
969         {
970           tCC*   pArg = *(ppArgs++);
971           char*  pz_scan_save;
972           if (pArg == NULL)
973             break;
974           *(pz_scan++) = ' ';
975           pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
976                                         parg_size - (pz_scan - pz_cmd) );
977           /*
978            *  Make sure we don't overflow the buffer due to sloppy
979            *  size estimation.
980            */
981           while (pz_scan == (char*)NULL)
982             {
983               size_t already_filled = pz_scan_save - pz_cmd;
984               pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
985               pz_scan_save = pz_scan = pz_cmd + already_filled;
986               parg_size += 100;
987               pz_scan = make_raw_shell_str( pz_scan, pArg,
988                                             parg_size - (pz_scan - pz_cmd) );
989             }
990         }
991
992       /*
993        *  add the file machinations.
994        */
995       sprintf( pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
996     }
997   system( pz_cmd );
998   free( (void*)pz_cmd );
999 }
1000
1001 /* * * * * * * * * * * * *
1002
1003     This loop should only cycle for 1/2 of one loop.
1004     "chain_open" starts a process that uses "read_fd" as
1005     its stdin and returns the new fd this process will use
1006     for stdout.  */
1007
1008 #else /* is *NOT* __MSDOS__ */
1009 int
1010 start_fixer (read_fd, p_fixd, pz_fix_file)
1011   int read_fd;
1012   tFixDesc* p_fixd;
1013   char* pz_fix_file;
1014 {
1015   tCC* pz_cmd_save;
1016   char* pz_cmd;
1017
1018   if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1019     return internal_fix (read_fd, p_fixd);
1020
1021   if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1022     pz_cmd = (char*)NULL;
1023   else
1024     {
1025       tSCC z_cmd_fmt[] = "file='%s'\n%s";
1026       pz_cmd = (char*)malloc (strlen (p_fixd->patch_args[2])
1027                                + sizeof( z_cmd_fmt )
1028                                + strlen( pz_fix_file ));
1029       if (pz_cmd == (char*)NULL)
1030         {
1031           fputs ("allocation failure\n", stderr);
1032           exit (EXIT_FAILURE);
1033         }
1034       sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1035       pz_cmd_save = p_fixd->patch_args[2];
1036       p_fixd->patch_args[2] = pz_cmd;
1037     }
1038
1039   /*  Start a fix process, handing off the  previous read fd for its
1040       stdin and getting a new fd that reads from the fix process' stdout.
1041       We normally will not loop, but we will up to 10 times if we keep
1042       getting "EAGAIN" errors.
1043
1044       */
1045   for (;;)
1046     {
1047       static int failCt = 0;
1048       int fd;
1049
1050       fd = chain_open (read_fd,
1051                        (t_pchar *) p_fixd->patch_args,
1052                        (process_chain_head == -1)
1053                        ? &process_chain_head : (pid_t *) NULL);
1054
1055       if (fd != -1)
1056         {
1057           read_fd = fd;
1058           break;
1059         }
1060
1061       fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1062                p_fixd->fix_name);
1063
1064       if ((errno != EAGAIN) || (++failCt > 10))
1065         exit (EXIT_FAILURE);
1066       sleep (1);
1067     }
1068
1069   /*  IF we allocated a shell script command,
1070       THEN free it and restore the command format to the fix description */
1071   if (pz_cmd != (char*)NULL)
1072     {
1073       free ((void*)pz_cmd);
1074       p_fixd->patch_args[2] = pz_cmd_save;
1075     }
1076
1077   return read_fd;
1078 }
1079 #endif
1080
1081
1082 /* * * * * * * * * * * * *
1083
1084    Process the potential fixes for a particular include file.
1085    Input:  the original text of the file and the file's name
1086    Result: none.  A new file may or may not be created.  */
1087
1088 t_bool
1089 fix_applies (p_fixd)
1090   tFixDesc *p_fixd;
1091 {
1092   const char *pz_fname = pz_curr_file;
1093   const char *pz_scan = p_fixd->file_list;
1094   int test_ct;
1095   tTestDesc *p_test;
1096
1097 # ifdef __MSDOS__
1098   /*
1099    *  There is only one fix that uses a shell script as of this writing.
1100    *  I hope to nuke it anyway, it does not apply to DOS and it would
1101    *  be painful to implement.  Therefore, no "shell" fixes for DOS.
1102    */
1103   if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1104     return BOOL_FALSE;
1105 # else
1106   if (p_fixd->fd_flags & FD_SKIP_TEST)
1107     return BOOL_FALSE;
1108 # endif
1109
1110   /*  IF there is a file name restriction,
1111       THEN ensure the current file name matches one in the pattern  */
1112
1113   if (pz_scan != (char *) NULL)
1114     {
1115       size_t name_len;
1116
1117       while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1118         pz_fname += 2;
1119       name_len = strlen (pz_fname);
1120
1121       for (;;)
1122         {
1123           pz_scan = strstr (pz_scan + 1, pz_fname);
1124           /*  IF we can't match the string at all,
1125               THEN bail  */
1126           if (pz_scan == (char *) NULL)
1127             return BOOL_FALSE;
1128
1129           /*  IF the match is surrounded by the '|' markers,
1130               THEN we found a full match -- time to run the tests  */
1131
1132           if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1133             break;
1134         }
1135     }
1136
1137   /*  FOR each test, see if it fails.
1138       IF it does fail, then we go on to the next test */
1139
1140   for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1141        test_ct-- > 0;
1142        p_test++)
1143     {
1144       switch (p_test->type)
1145         {
1146         case TT_TEST:
1147           if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1148 #ifdef DEBUG
1149             if (VLEVEL( VERB_EVERYTHING ))
1150               fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1151                        pz_fname, p_fixd->test_ct - test_ct);
1152 #endif
1153             return BOOL_FALSE;
1154           }
1155           break;
1156
1157         case TT_EGREP:
1158           if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1159 #ifdef DEBUG
1160             if (VLEVEL( VERB_EVERYTHING ))
1161               fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1162                        pz_fname, p_fixd->test_ct - test_ct);
1163 #endif
1164             return BOOL_FALSE;
1165           }
1166           break;
1167
1168         case TT_NEGREP:
1169           if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1170 #ifdef DEBUG
1171             if (VLEVEL( VERB_EVERYTHING ))
1172               fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1173                        pz_fname, p_fixd->test_ct - test_ct);
1174 #endif
1175             /*  Negated sense  */
1176             return BOOL_FALSE;
1177           }
1178           break;
1179
1180         case TT_FUNCTION:
1181           if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1182               != APPLY_FIX) {
1183 #ifdef DEBUG
1184             if (VLEVEL( VERB_EVERYTHING ))
1185               fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1186                        pz_fname, p_fixd->test_ct - test_ct);
1187 #endif
1188             return BOOL_FALSE;
1189           }
1190           break;
1191         }
1192     }
1193
1194   return BOOL_TRUE;
1195 }
1196
1197
1198 /* * * * * * * * * * * * *
1199
1200    Write out a replacement file  */
1201
1202 void
1203 write_replacement (p_fixd)
1204   tFixDesc *p_fixd;
1205 {
1206    const char* pz_text = p_fixd->patch_args[0];
1207
1208    if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1209      return;
1210
1211    {
1212      FILE* out_fp = create_file (pz_curr_file);
1213      fputs (pz_text, out_fp);
1214      fclose (out_fp);
1215    }
1216 }
1217
1218
1219 /* * * * * * * * * * * * *
1220
1221     We have work to do.  Read back in the output
1222     of the filtering chain.  Compare each byte as we read it with
1223     the contents of the original file.  As soon as we find any
1224     difference, we will create the output file, write out all
1225     the matched text and then copy any remaining data from the
1226     output of the filter chain.
1227     */
1228 void
1229 test_for_changes (read_fd)
1230   int read_fd;
1231 {
1232   FILE *in_fp = fdopen (read_fd, "r");
1233   FILE *out_fp = (FILE *) NULL;
1234   char *pz_cmp = pz_curr_data;
1235
1236 #ifdef DO_STATS
1237   fixed_ct++;
1238 #endif
1239   for (;;)
1240     {
1241       int ch;
1242
1243       ch = getc (in_fp);
1244       if (ch == EOF)
1245         break;
1246
1247       /*  IF we are emitting the output
1248           THEN emit this character, too.
1249       */
1250       if (out_fp != (FILE *) NULL)
1251         putc (ch, out_fp);
1252
1253       /*  ELSE if this character does not match the original,
1254           THEN now is the time to start the output.
1255       */
1256       else if (ch != *pz_cmp)
1257         {
1258           out_fp = create_file (pz_curr_file);
1259
1260 #ifdef DO_STATS
1261           altered_ct++;
1262 #endif
1263           /*  IF there are matched data, write the matched part now. */
1264           if (pz_cmp != pz_curr_data)
1265             fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
1266
1267           /*  Emit the current unmatching character */
1268           putc (ch, out_fp);
1269         }
1270       else
1271         /*  ELSE the character matches.  Advance the compare ptr */
1272         pz_cmp++;
1273     }
1274
1275   /*  IF we created the output file, ... */
1276   if (out_fp != (FILE *) NULL)
1277     {
1278       regmatch_t match;
1279
1280       /* Close the file and see if we have to worry about
1281          `#include "file.h"' constructs.  */
1282       fclose (out_fp);
1283       if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1284         extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1285     }
1286
1287   fclose (in_fp);
1288   close (read_fd);  /* probably redundant, but I'm paranoid */
1289 }
1290
1291
1292 /* * * * * * * * * * * * *
1293
1294    Process the potential fixes for a particular include file.
1295    Input:  the original text of the file and the file's name
1296    Result: none.  A new file may or may not be created.  */
1297
1298 void
1299 process ()
1300 {
1301   tFixDesc *p_fixd = fixDescList;
1302   int todo_ct = FIX_COUNT;
1303   int read_fd = -1;
1304 # ifndef __MSDOS__
1305   int num_children = 0;
1306 # else /* is __MSDOS__ */
1307   char* pz_file_source = pz_curr_file;
1308 # endif
1309
1310   if (access (pz_curr_file, R_OK) != 0)
1311     {
1312       int erno = errno;
1313       fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1314                pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1315                erno, xstrerror (erno));
1316       return;
1317     }
1318
1319   pz_curr_data = load_file (pz_curr_file);
1320   if (pz_curr_data == (char *) NULL)
1321     return;
1322
1323 #ifdef DO_STATS
1324   process_ct++;
1325 #endif
1326   if (VLEVEL( VERB_PROGRESS ) && have_tty)
1327     fprintf (stderr, "%6d %-50s   \r", data_map_size, pz_curr_file );
1328
1329 # ifndef __MSDOS__
1330   process_chain_head = NOPROCESS;
1331
1332   /* For every fix in our fix list, ...  */
1333   for (; todo_ct > 0; p_fixd++, todo_ct--)
1334     {
1335       if (! fix_applies (p_fixd))
1336         continue;
1337
1338       if (VLEVEL( VERB_APPLIES ))
1339         fprintf (stderr, "Applying %-24s to %s\n",
1340                  p_fixd->fix_name, pz_curr_file);
1341
1342       if (p_fixd->fd_flags & FD_REPLACEMENT)
1343         {
1344           write_replacement (p_fixd);
1345           UNLOAD_DATA();
1346           return;
1347         }
1348
1349       /*  IF we do not have a read pointer,
1350           THEN this is the first fix for the current file.
1351           Open the source file.  That will be used as stdin for
1352           the first fix.  Any subsequent fixes will use the
1353           stdout descriptor of the previous fix for its stdin.  */
1354
1355       if (read_fd == -1)
1356         {
1357           read_fd = open (pz_curr_file, O_RDONLY);
1358           if (read_fd < 0)
1359             {
1360               fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1361                        xstrerror (errno), pz_curr_file);
1362               exit (EXIT_FAILURE);
1363             }
1364
1365           /*  Ensure we do not get duplicate output */
1366
1367           fflush (stdout);
1368         }
1369
1370       read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1371       num_children++;
1372     }
1373
1374   /*  IF we have a read-back file descriptor,
1375       THEN check for changes and write output if changed.   */
1376
1377   if (read_fd >= 0)
1378     {
1379       test_for_changes (read_fd);
1380 #ifdef DO_STATS
1381       apply_ct += num_children;
1382 #endif
1383       /* Wait for child processes created by chain_open()
1384          to avoid leaving zombies.  */
1385       do  {
1386         wait ((int *) NULL);
1387       } while (--num_children > 0);
1388     }
1389
1390 # else /* is __MSDOS__ */
1391
1392   for (; todo_ct > 0; p_fixd++, todo_ct--)
1393     {
1394       if (! fix_applies (p_fixd))
1395         continue;
1396
1397       if (VLEVEL( VERB_APPLIES ))
1398         fprintf (stderr, "Applying %-24s to %s\n",
1399                  p_fixd->fix_name, pz_curr_file);
1400
1401       if (p_fixd->fd_flags & FD_REPLACEMENT)
1402         {
1403           write_replacement (p_fixd);
1404           UNLOAD_DATA();
1405           return;
1406         }
1407       fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1408       pz_file_source = pz_temp_file;
1409     }
1410
1411   read_fd = open( pz_temp_file, O_RDONLY );
1412   test_for_changes( read_fd );
1413   /* Unlinking a file while it is still open is a Bad Idea on
1414      DOS/Windows.  */
1415   close( read_fd );
1416   unlink( pz_temp_file );
1417
1418 # endif
1419   UNLOAD_DATA();
1420 }