OSDN Git Service

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