OSDN Git Service

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