OSDN Git Service

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