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