OSDN Git Service

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