OSDN Git Service

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