]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - fixincludes/fixincl.c
Imported gcc-4.4.3
[msp430-gcc.git] / fixincludes / fixincl.c
diff --git a/fixincludes/fixincl.c b/fixincludes/fixincl.c
new file mode 100644 (file)
index 0000000..9f399ab
--- /dev/null
@@ -0,0 +1,1358 @@
+/* Install modified versions of certain ANSI-incompatible system header
+   files which are fixed to work correctly with ANSI C and placed in a
+   directory that GCC will search.
+
+   Copyright (C) 1997, 1998, 1999, 2000, 2004, 2009
+   Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "fixlib.h"
+
+#include <fnmatch.h>
+#include <sys/stat.h>
+#ifndef SEPARATE_FIX_PROC
+#include <sys/wait.h>
+#endif
+
+#if defined( HAVE_MMAP_FILE )
+#include <sys/mman.h>
+#define  BAD_ADDR ((void*)-1)
+#endif
+
+#ifndef SEPARATE_FIX_PROC
+#include "server.h"
+#endif
+
+/*  The contents of this string are not very important.  It is mostly
+    just used as part of the "I am alive and working" test.  */
+
+static const char program_id[] = "fixincl version 1.1";
+
+/*  This format will be used at the start of every generated file */
+
+static const char z_std_preamble[] =
+"/*  DO NOT EDIT THIS FILE.\n\n\
+    It has been auto-edited by fixincludes from:\n\n\
+\t\"%s/%s\"\n\n\
+    This had to be done to correct non-standard usages in the\n\
+    original, manufacturer supplied header file.  */\n\n";
+
+int find_base_len = 0;
+
+typedef enum {
+  VERB_SILENT = 0,
+  VERB_FIXES,
+  VERB_APPLIES,
+  VERB_PROGRESS,
+  VERB_TESTS,
+  VERB_EVERYTHING
+} te_verbose;
+
+te_verbose  verbose_level = VERB_PROGRESS;
+int have_tty = 0;
+
+#define VLEVEL(l)  ((unsigned int) verbose_level >= (unsigned int) l)
+#define NOT_SILENT VLEVEL(VERB_FIXES)
+
+pid_t process_chain_head = (pid_t) -1;
+
+char*  pz_curr_file;  /*  name of the current file under test/fix  */
+char*  pz_curr_data;  /*  original contents of that file  */
+char*  pz_temp_file;  /*  for DOS, a place to stash the temporary
+                          fixed data between system(3) calls  */
+t_bool curr_data_mapped;
+int    data_map_fd;
+size_t data_map_size;
+size_t ttl_data_size = 0;
+
+#ifdef DO_STATS
+int process_ct = 0;
+int apply_ct = 0;
+int fixed_ct = 0;
+int altered_ct = 0;
+#endif /* DO_STATS */
+
+const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
+tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
+regex_t incl_quote_re;
+
+static void do_version (void) ATTRIBUTE_NORETURN;
+char *load_file (const char *);
+void run_compiles (void);
+void initialize (int argc, char** argv);
+void process (void);
+
+/*  External Source Code */
+
+#include "fixincl.x"
+
+/* * * * * * * * * * * * * * * * * * *
+ *
+ *  MAIN ROUTINE
+ */
+extern int main (int, char **);
+int
+main (int argc, char** argv)
+{
+  char *file_name_buf;
+
+  initialize ( argc, argv );
+
+  have_tty = isatty (fileno (stderr));
+
+  /* Before anything else, ensure we can allocate our file name buffer. */
+  file_name_buf = load_file_data (stdin);
+
+  /*  Because of the way server shells work, you have to keep stdin, out
+      and err open so that the proper input file does not get closed
+      by accident  */
+
+  freopen ("/dev/null", "r", stdin);
+
+  if (file_name_buf == (char *) NULL)
+    {
+      fputs ("No file names listed for fixing\n", stderr);
+      exit (EXIT_FAILURE);
+    }
+
+  for (;;)
+    {
+      char* pz_end;
+
+      /*  skip to start of name, past any "./" prefixes */
+
+      while (ISSPACE (*file_name_buf))  file_name_buf++;
+      while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
+        file_name_buf += 2;
+
+      /*  Check for end of list  */
+
+      if (*file_name_buf == NUL)
+        break;
+
+      /*  Set global file name pointer and find end of name */
+
+      pz_curr_file = file_name_buf;
+      pz_end = strchr( pz_curr_file, '\n' );
+      if (pz_end == (char*)NULL)
+        pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
+      else
+        file_name_buf = pz_end + 1;
+
+      while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1]))  pz_end--;
+
+      /*  IF no name is found (blank line) or comment marker, skip line  */
+
+      if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
+        continue;
+      *pz_end = NUL;
+
+      process ();
+    } /*  for (;;) */
+
+#ifdef DO_STATS
+  if (VLEVEL( VERB_PROGRESS )) {
+    tSCC zFmt[] =
+      "\
+Processed %5d files containing %d bytes    \n\
+Applying  %5d fixes to %d files\n\
+Altering  %5d of them\n";
+
+    fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
+             fixed_ct, altered_ct);
+  }
+#endif /* DO_STATS */
+
+# ifdef SEPARATE_FIX_PROC
+  unlink( pz_temp_file );
+# endif
+  exit (EXIT_SUCCESS);
+}
+
+
+static void
+do_version (void)
+{
+  static const char zFmt[] = "echo '%s'";
+  char zBuf[ 1024 ];
+
+  /* The 'version' option is really used to test that:
+     1.  The program loads correctly (no missing libraries)
+     2.  that we can compile all the regular expressions.
+     3.  we can correctly run our server shell process
+  */
+  run_compiles ();
+  sprintf (zBuf, zFmt, program_id);
+#ifndef SEPARATE_FIX_PROC
+  puts (zBuf + 5);
+  exit (strcmp (run_shell (zBuf), program_id));
+#else
+  exit (system (zBuf));
+#endif
+}
+
+/* * * * * * * * * * * * */
+
+void
+initialize ( int argc, char** argv )
+{
+  xmalloc_set_program_name (argv[0]);
+
+  switch (argc)
+    {
+    case 1:
+      break;
+
+    case 2:
+      if (strcmp (argv[1], "-v") == 0)
+        do_version ();
+      if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
+        {
+          fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
+                   errno, xstrerror (errno), argv[1] );
+          exit (EXIT_FAILURE);
+        }
+      break;
+
+    default:
+      fputs ("fixincl ERROR:  too many command line arguments\n", stderr);
+      exit (EXIT_FAILURE);
+    }
+
+#ifdef SIGCHLD
+  /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
+     receive the signal.  A different setting is inheritable */
+  signal (SIGCHLD, SIG_DFL);
+#endif
+
+  initialize_opts ();
+
+  if (ISDIGIT ( *pz_verbose ))
+    verbose_level = (te_verbose)atoi( pz_verbose );
+  else
+    switch (*pz_verbose) {
+    case 's':
+    case 'S':
+      verbose_level = VERB_SILENT;     break;
+
+    case 'f':
+    case 'F':
+      verbose_level = VERB_FIXES;      break;
+
+    case 'a':
+    case 'A':
+      verbose_level = VERB_APPLIES;    break;
+
+    default:
+    case 'p':
+    case 'P':
+      verbose_level = VERB_PROGRESS;   break;
+
+    case 't':
+    case 'T':
+      verbose_level = VERB_TESTS;      break;
+
+    case 'e':
+    case 'E':
+      verbose_level = VERB_EVERYTHING; break;
+    }
+  if (verbose_level >= VERB_EVERYTHING) {
+    verbose_level = VERB_EVERYTHING;
+    fputs ("fixinc verbosity:  EVERYTHING\n", stderr);
+  }
+  while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
+    pz_find_base += 2;
+  if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
+    find_base_len = strlen( pz_find_base );
+
+  /*  Compile all the regular expressions now.
+      That way, it is done only once for the whole run.
+      */
+  run_compiles ();
+
+# ifdef SEPARATE_FIX_PROC
+  /* NULL as the first argument to `tempnam' causes it to DTRT
+     wrt the temporary directory where the file will be created.  */
+  pz_temp_file = tempnam( NULL, "fxinc" );
+# endif
+
+  signal (SIGQUIT, SIG_IGN);
+  signal (SIGIOT,  SIG_IGN);
+  signal (SIGPIPE, SIG_IGN);
+  signal (SIGALRM, SIG_IGN);
+  signal (SIGTERM, SIG_IGN);
+}
+
+/* * * * * * * * * * * * *
+
+   load_file loads all the contents of a file into malloc-ed memory.
+   Its argument is the name of the file to read in; the returned
+   result is the NUL terminated contents of the file.  The file
+   is presumed to be an ASCII text file containing no NULs.  */
+char *
+load_file ( const char* fname )
+{
+  struct stat stbf;
+  char* res;
+
+  if (stat (fname, &stbf) != 0)
+    {
+      if (NOT_SILENT)
+        fprintf (stderr, "error %d (%s) stat-ing %s\n",
+                 errno, xstrerror (errno), fname );
+      return (char *) NULL;
+    }
+  if (stbf.st_size == 0)
+    return (char*)NULL;
+
+  /*  Make the data map size one larger than the file size for documentation
+      purposes.  Truth is that there will be a following NUL character if
+      the file size is not a multiple of the page size.  If it is a multiple,
+      then this adjustment sometimes fails anyway.  */
+  data_map_size = stbf.st_size+1;
+  data_map_fd   = open (fname, O_RDONLY);
+  ttl_data_size += data_map_size-1;
+
+  if (data_map_fd < 0)
+    {
+      if (NOT_SILENT)
+        fprintf (stderr, "error %d (%s) opening %s for read\n",
+                 errno, xstrerror (errno), fname);
+      return (char*)NULL;
+    }
+
+#ifdef HAVE_MMAP_FILE
+  curr_data_mapped = BOOL_TRUE;
+
+  /*  IF the file size is a multiple of the page size,
+      THEN sometimes you will seg fault trying to access a trailing byte */
+  if ((stbf.st_size & (getpagesize()-1)) == 0)
+    res = (char*)BAD_ADDR;
+  else
+    res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
+                       MAP_PRIVATE, data_map_fd, 0);
+  if (res == (char*)BAD_ADDR)
+#endif
+    {
+      FILE* fp = fdopen (data_map_fd, "r");
+      curr_data_mapped = BOOL_FALSE;
+      res = load_file_data (fp);
+      fclose (fp);
+    }
+
+  return res;
+}
+
+static int
+machine_matches( tFixDesc* p_fixd )
+{
+  char const ** papz_machs = p_fixd->papz_machs;
+  int have_match = BOOL_FALSE;
+
+  for (;;)
+    {
+      char const * pz_mpat = *(papz_machs++);
+      if (pz_mpat == NULL)
+        break;
+      if (fnmatch(pz_mpat, pz_machine, 0) == 0)
+        {
+          have_match = BOOL_TRUE;
+          break;
+        }
+    }
+
+  /* Check for sense inversion then set the "skip test" flag, if needed */
+  if (p_fixd->fd_flags & FD_MACH_IFNOT)
+    have_match = ! have_match;
+
+  if (! have_match)
+    p_fixd->fd_flags |= FD_SKIP_TEST;
+
+  return have_match;
+}
+
+/* * * * * * * * * * * * *
+ *
+ *  run_compiles   run all the regexp compiles for all the fixes once.
+ */
+void
+run_compiles (void)
+{
+  tFixDesc *p_fixd = fixDescList;
+  int fix_ct = FIX_COUNT;
+  regex_t *p_re = XCNEWVEC (regex_t, REGEX_COUNT);
+
+  /*  Make sure compile_re does not stumble across invalid data */
+
+  memset (&incl_quote_re, '\0', sizeof (regex_t));
+
+  compile_re (incl_quote_pat, &incl_quote_re, 1,
+              "quoted include", "run_compiles");
+
+  /*  Allow machine name tests to be ignored (testing, mainly) */
+
+  if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
+    pz_machine = (char*)NULL;
+
+  /* FOR every fixup, ...  */
+  do
+    {
+      tTestDesc *p_test = p_fixd->p_test_desc;
+      int test_ct = p_fixd->test_ct;
+
+      /*  IF the machine type pointer is not NULL (we are not in test mode)
+             AND this test is for or not done on particular machines
+          THEN ...   */
+
+      if (  (pz_machine != NULL)
+         && (p_fixd->papz_machs != (const char**) NULL)
+         && ! machine_matches (p_fixd) )
+        continue;
+
+      /* FOR every test for the fixup, ...  */
+
+      while (--test_ct >= 0)
+        {
+          switch (p_test->type)
+            {
+            case TT_EGREP:
+            case TT_NEGREP:
+              p_test->p_test_regex = p_re++;
+              compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
+                          "select test", p_fixd->fix_name);
+            default: break;
+            }
+          p_test++;
+        }
+    }
+  while (p_fixd++, --fix_ct > 0);
+}
+
+
+/* * * * * * * * * * * * *
+
+   create_file  Create the output modified file.
+   Input:    the name of the file to create
+   Returns:  a file pointer to the new, open file  */
+
+#if defined(S_IRUSR) && defined(S_IWUSR) && \
+    defined(S_IRGRP) && defined(S_IROTH)
+
+#   define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#else
+#   define S_IRALL 0644
+#endif
+
+#if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
+    defined(S_IROTH) && defined(S_IXOTH)
+
+#   define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+#else
+#   define S_DIRALL 0755
+#endif
+
+
+static FILE *
+create_file (void)
+{
+  int fd;
+  FILE *pf;
+  char fname[MAXPATHLEN];
+
+  sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
+
+  fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
+
+  /*  We may need to create the directories needed... */
+  if ((fd < 0) && (errno == ENOENT))
+    {
+      char *pz_dir = strchr (fname + 1, '/');
+      struct stat stbf;
+
+      while (pz_dir != (char *) NULL)
+        {
+          *pz_dir = NUL;
+          if (stat (fname, &stbf) < 0)
+            {
+#ifdef _WIN32
+              mkdir (fname);
+#else
+              mkdir (fname, S_IFDIR | S_DIRALL);
+#endif
+            }
+
+          *pz_dir = '/';
+          pz_dir = strchr (pz_dir + 1, '/');
+        }
+
+      /*  Now, lets try the open again... */
+      fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
+    }
+  if (fd < 0)
+    {
+      fprintf (stderr, "Error %d (%s) creating %s\n",
+               errno, xstrerror (errno), fname);
+      exit (EXIT_FAILURE);
+    }
+  if (NOT_SILENT)
+    fprintf (stderr, "Fixed:  %s\n", pz_curr_file);
+  pf = fdopen (fd, "w");
+
+  /*
+   *  IF pz_machine is NULL, then we are in some sort of test mode.
+   *  Do not insert the current directory name.  Use a constant string.
+   */
+  fprintf (pf, z_std_preamble,
+           (pz_machine == NULL)
+           ? "fixinc/tests/inc"
+           : pz_input_dir,
+           pz_curr_file);
+
+  return pf;
+}
+
+
+/* * * * * * * * * * * * *
+
+  test_test   make sure a shell-style test expression passes.
+  Input:  a pointer to the descriptor of the test to run and
+          the name of the file that we might want to fix
+  Result: APPLY_FIX or SKIP_FIX, depending on the result of the
+          shell script we run.  */
+#ifndef SEPARATE_FIX_PROC
+static int
+test_test (tTestDesc* p_test, char* pz_test_file)
+{
+  tSCC cmd_fmt[] =
+"file=%s\n\
+if ( test %s ) > /dev/null 2>&1\n\
+then echo TRUE\n\
+else echo FALSE\n\
+fi";
+
+  char *pz_res;
+  int res;
+
+  static char cmd_buf[4096];
+
+  sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
+  pz_res = run_shell (cmd_buf);
+
+  switch (*pz_res) {
+  case 'T':
+    res = APPLY_FIX;
+    break;
+
+  case 'F':
+    res = SKIP_FIX;
+    break;
+
+  default:
+    fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
+             pz_res, cmd_buf );
+    res = SKIP_FIX;
+  }
+
+  free ((void *) pz_res);
+  return res;
+}
+#else
+/*
+ *  IF we are in MS-DOS land, then whatever shell-type test is required
+ *  will, by definition, fail
+ */
+#define test_test(t,tf)  SKIP_FIX
+#endif
+
+/* * * * * * * * * * * * *
+
+  egrep_test   make sure an egrep expression is found in the file text.
+  Input:  a pointer to the descriptor of the test to run and
+          the pointer to the contents of the file under suspicion
+  Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
+
+  The caller may choose to reverse meaning if the sense of the test
+  is inverted.  */
+
+static int
+egrep_test (char* pz_data, tTestDesc* p_test)
+{
+#ifdef DEBUG
+  if (p_test->p_test_regex == 0)
+    fprintf (stderr, "fixincl ERROR RE not compiled:  `%s'\n",
+             p_test->pz_test_text);
+#endif
+  if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
+    return APPLY_FIX;
+  return SKIP_FIX;
+}
+
+
+/* * * * * * * * * * * * *
+
+  quoted_file_exists  Make sure that a file exists before we emit
+  the file name.  If we emit the name, our invoking shell will try
+  to copy a non-existing file into the destination directory.  */
+
+static int
+quoted_file_exists (const char* pz_src_path,
+                    const char* pz_file_path, 
+                    const char* pz_file)
+{
+  char z[ MAXPATHLEN ];
+  char* pz;
+  sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
+  pz = z + strlen ( z );
+
+  for (;;) {
+    char ch = *pz_file++;
+    if (! ISGRAPH( ch ))
+      return 0;
+    if (ch == '"')
+      break;
+    *pz++ = ch;
+  }
+  *pz = '\0';
+  {
+    struct stat s;
+    if (stat (z, &s) != 0)
+      return 0;
+    return S_ISREG( s.st_mode );
+  }
+}
+
+
+/* * * * * * * * * * * * *
+ *
+   extract_quoted_files
+
+   The syntax, `#include "file.h"' specifies that the compiler is to
+   search the local directory of the current file before the include
+   list.  Consequently, if we have modified a header and stored it in
+   another directory, any files that are included by that modified
+   file in that fashion must also be copied into this new directory.
+   This routine finds those flavors of #include and for each one found
+   emits a triple of:
+
+    1.  source directory of the original file
+    2.  the relative path file name of the #includ-ed file
+    3.  the full destination path for this file
+
+   Input:  the text of the file, the file name and a pointer to the
+           match list where the match information was stored.
+   Result: internally nothing.  The results are written to stdout
+           for interpretation by the invoking shell  */
+
+
+static void
+extract_quoted_files (char* pz_data, 
+                      const char* pz_fixed_file,
+                      regmatch_t* p_re_match)
+{
+  char *pz_dir_end = strrchr (pz_fixed_file, '/');
+  char *pz_incl_quot = pz_data;
+
+  if (VLEVEL( VERB_APPLIES ))
+    fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
+
+  /*  Set "pz_fixed_file" to point to the containing subdirectory of the source
+      If there is none, then it is in our current directory, ".".   */
+
+  if (pz_dir_end == (char *) NULL)
+    pz_fixed_file = ".";
+  else
+    *pz_dir_end = '\0';
+
+  for (;;)
+    {
+      pz_incl_quot += p_re_match->rm_so;
+
+      /*  Skip forward to the included file name */
+      while (*pz_incl_quot != '"')
+        pz_incl_quot++;
+
+      if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
+        {
+          /* Print the source directory and the subdirectory
+             of the file in question.  */
+          printf ("%s  %s/", pz_src_dir, pz_fixed_file);
+          pz_dir_end = pz_incl_quot;
+
+          /* Append to the directory the relative path of the desired file */
+          while (*pz_incl_quot != '"')
+            putc (*pz_incl_quot++, stdout);
+
+          /* Now print the destination directory appended with the
+             relative path of the desired file */
+          printf ("  %s/%s/", pz_dest_dir, pz_fixed_file);
+          while (*pz_dir_end != '"')
+            putc (*pz_dir_end++, stdout);
+
+          /* End of entry */
+          putc ('\n', stdout);
+        }
+
+      /* Find the next entry */
+      if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
+        break;
+    }
+}
+
+
+/* * * * * * * * * * * * *
+
+    Somebody wrote a *_fix subroutine that we must call.
+    */
+#ifndef SEPARATE_FIX_PROC
+static int
+internal_fix (int read_fd, tFixDesc* p_fixd)
+{
+  int fd[2];
+
+  if (pipe( fd ) != 0)
+    {
+      fprintf (stderr, "Error %d on pipe(2) call\n", errno );
+      exit (EXIT_FAILURE);
+    }
+
+  for (;;)
+    {
+      pid_t childid = fork();
+
+      switch (childid)
+        {
+        case -1:
+          break;
+
+        case 0:
+          close (fd[0]);
+          goto do_child_task;
+
+        default:
+          /*
+           *  Parent process
+           */
+          close (read_fd);
+          close (fd[1]);
+          return fd[0];
+        }
+
+      /*
+       *  Parent in error
+       */
+      fprintf (stderr, z_fork_err, errno, xstrerror (errno),
+               p_fixd->fix_name);
+      {
+        static int failCt = 0;
+        if ((errno != EAGAIN) || (++failCt > 10))
+          exit (EXIT_FAILURE);
+        sleep (1);
+      }
+    } do_child_task:;
+
+  /*
+   *  Close our current stdin and stdout
+   */
+  close (STDIN_FILENO);
+  close (STDOUT_FILENO);
+  UNLOAD_DATA();
+
+  /*
+   *  Make the fd passed in the stdin, and the write end of
+   *  the new pipe become the stdout.
+   */
+  dup2 (fd[1], STDOUT_FILENO);
+  dup2 (read_fd, STDIN_FILENO);
+
+  apply_fix (p_fixd, pz_curr_file);
+  exit (0);
+}
+#endif /* !SEPARATE_FIX_PROC */
+
+
+#ifdef SEPARATE_FIX_PROC
+static void
+fix_with_system (tFixDesc* p_fixd,
+                 tCC* pz_fix_file,
+                 tCC* pz_file_source,
+                 tCC* pz_temp_file)
+{
+  char*  pz_cmd;
+  char*  pz_scan;
+  size_t argsize;
+
+  if (p_fixd->fd_flags & FD_SUBROUTINE)
+    {
+      static const char z_applyfix_prog[] =
+       "/../fixincludes/applyfix" EXE_EXT;
+
+      struct stat buf;
+      argsize = 32
+              + strlen (pz_orig_dir)
+              + sizeof (z_applyfix_prog)
+              + strlen (pz_fix_file)
+              + strlen (pz_file_source)
+              + strlen (pz_temp_file);
+
+      /* Allocate something sure to be big enough for our purposes */
+      pz_cmd = XNEWVEC (char, argsize);
+      strcpy (pz_cmd, pz_orig_dir);
+      pz_scan = pz_cmd + strlen (pz_orig_dir);
+
+      strcpy (pz_scan, z_applyfix_prog);
+
+      /* IF we can't find the "applyfix" executable file at the first guess,
+        try one level higher up  */
+      if (stat (pz_cmd, &buf) == -1)
+       {
+         strcpy (pz_scan, "/..");
+         strcpy (pz_scan+3, z_applyfix_prog);
+       }
+
+      pz_scan += strlen (pz_scan);
+
+      /*
+       *  Now add the fix number and file names that may be needed
+       */
+      sprintf (pz_scan, " %ld '%s' '%s'",  (long) (p_fixd - fixDescList),
+              pz_fix_file, pz_file_source, pz_temp_file);
+    }
+  else /* NOT an "internal" fix: */
+    {
+      size_t parg_size;
+#ifdef __MSDOS__
+      /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
+         dst is a temporary file anyway, so we know there's no other
+         file by that name; and DOS's system(3) doesn't mind to
+         clobber existing file in redirection.  Besides, with DOS 8+3
+         limited file namespace, we can easily lose if dst already has
+         an extension that is 3 or more characters long.
+
+         I do not think the 8+3 issue is relevant because all the files
+         we operate on are named "*.h", making 8+2 adequate.  Anyway,
+         the following bizarre use of 'cat' only works on DOS boxes.
+         It causes the file to be dropped into a temporary file for
+         'cat' to read (pipes do not work on DOS).  */
+      tSCC   z_cmd_fmt[] = " '%s' | cat > '%s'";
+#else
+      /* Don't use positional formatting arguments because some lame-o
+         implementations cannot cope  :-(.  */
+      tSCC   z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
+#endif
+      tCC**  ppArgs = p_fixd->patch_args;
+
+      argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
+              + strlen( pz_file_source );
+      parg_size = argsize;
+      
+
+      /*
+       *  Compute the size of the command line.  Add lotsa extra space
+       *  because some of the args to sed use lotsa single quotes.
+       *  (This requires three extra bytes per quote.  Here we allow
+       *  for up to 8 single quotes for each argument, including the
+       *  command name "sed" itself.  Nobody will *ever* need more. :)
+       */
+      for (;;)
+        {
+          tCC* p_arg = *(ppArgs++);
+          if (p_arg == NULL)
+            break;
+          argsize += 24 + strlen( p_arg );
+        }
+
+      /* Estimated buffer size we will need.  */
+      pz_scan = pz_cmd = XNEWVEC (char, argsize);
+      /* How much of it do we allot to the program name and its
+         arguments.  */
+      parg_size = argsize - parg_size;
+
+      ppArgs = p_fixd->patch_args;
+
+      /*
+       *  Copy the program name, unquoted
+       */
+      {
+        tCC*   pArg = *(ppArgs++);
+        for (;;)
+          {
+            char ch = *(pArg++);
+            if (ch == NUL)
+              break;
+            *(pz_scan++) = ch;
+          }
+      }
+
+      /*
+       *  Copy the program arguments, quoted
+       */
+      for (;;)
+        {
+          tCC*   pArg = *(ppArgs++);
+         char*  pz_scan_save;
+          if (pArg == NULL)
+            break;
+          *(pz_scan++) = ' ';
+          pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
+                                       parg_size - (pz_scan - pz_cmd) );
+         /*
+          *  Make sure we don't overflow the buffer due to sloppy
+          *  size estimation.
+          */
+         while (pz_scan == (char*)NULL)
+           {
+             size_t already_filled = pz_scan_save - pz_cmd;
+             pz_cmd = xrealloc (pz_cmd, argsize += 100);
+             pz_scan_save = pz_scan = pz_cmd + already_filled;
+             parg_size += 100;
+             pz_scan = make_raw_shell_str( pz_scan, pArg,
+                                           parg_size - (pz_scan - pz_cmd) );
+           }
+        }
+
+      /*
+       *  add the file machinations.
+       */
+#ifdef __MSDOS__
+      sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
+#else
+      sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
+               pz_temp_file, pz_temp_file, pz_temp_file);
+#endif
+    }
+  system( pz_cmd );
+  free( (void*)pz_cmd );
+}
+
+/* * * * * * * * * * * * *
+
+    This loop should only cycle for 1/2 of one loop.
+    "chain_open" starts a process that uses "read_fd" as
+    its stdin and returns the new fd this process will use
+    for stdout.  */
+
+#else /* is *NOT* SEPARATE_FIX_PROC */
+static int
+start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
+{
+  tCC* pz_cmd_save;
+  char* pz_cmd;
+
+  if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
+    return internal_fix (read_fd, p_fixd);
+
+  if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
+    {
+      pz_cmd = NULL;
+      pz_cmd_save = NULL;
+    }
+  else
+    {
+      tSCC z_cmd_fmt[] = "file='%s'\n%s";
+      pz_cmd = XNEWVEC (char, strlen (p_fixd->patch_args[2])
+                       + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
+      sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
+      pz_cmd_save = p_fixd->patch_args[2];
+      p_fixd->patch_args[2] = pz_cmd;
+    }
+
+  /*  Start a fix process, handing off the  previous read fd for its
+      stdin and getting a new fd that reads from the fix process' stdout.
+      We normally will not loop, but we will up to 10 times if we keep
+      getting "EAGAIN" errors.
+
+      */
+  for (;;)
+    {
+      static int failCt = 0;
+      int fd;
+
+      fd = chain_open (read_fd,
+                       (tCC **) p_fixd->patch_args,
+                       (process_chain_head == -1)
+                       ? &process_chain_head : (pid_t *) NULL);
+
+      if (fd != -1)
+        {
+          read_fd = fd;
+          break;
+        }
+
+      fprintf (stderr, z_fork_err, errno, xstrerror (errno),
+               p_fixd->fix_name);
+
+      if ((errno != EAGAIN) || (++failCt > 10))
+        exit (EXIT_FAILURE);
+      sleep (1);
+    }
+
+  /*  IF we allocated a shell script command,
+      THEN free it and restore the command format to the fix description */
+  if (pz_cmd != (char*)NULL)
+    {
+      free ((void*)pz_cmd);
+      p_fixd->patch_args[2] = pz_cmd_save;
+    }
+
+  return read_fd;
+}
+#endif
+
+
+/* * * * * * * * * * * * *
+ *
+ *  Process the potential fixes for a particular include file.
+ *  Input:  the original text of the file and the file's name
+ *  Result: none.  A new file may or may not be created.
+ */
+static t_bool
+fix_applies (tFixDesc* p_fixd)
+{
+  const char *pz_fname = pz_curr_file;
+  const char *pz_scan = p_fixd->file_list;
+  int test_ct;
+  tTestDesc *p_test;
+
+#ifdef SEPARATE_FIX_PROC
+  /*
+   *  There is only one fix that uses a shell script as of this writing.
+   *  I hope to nuke it anyway, it does not apply to DOS and it would
+   *  be painful to implement.  Therefore, no "shell" fixes for DOS.
+   */
+  if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
+    return BOOL_FALSE;
+#else
+  if (p_fixd->fd_flags & FD_SKIP_TEST)
+    return BOOL_FALSE;
+#endif
+
+  /*  IF there is a file name restriction,
+      THEN ensure the current file name matches one in the pattern  */
+
+  if (pz_scan != (char *) NULL)
+    {
+      while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
+        pz_fname += 2;
+
+      for (;;)
+        {
+          if (fnmatch (pz_scan, pz_fname, 0) == 0)
+            break;
+          pz_scan += strlen (pz_scan) + 1;
+          if (*pz_scan == NUL)
+            return BOOL_FALSE;
+        }
+    }
+
+  /*  FOR each test, see if it fails.
+      IF it does fail, then we go on to the next test */
+
+  for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
+       test_ct-- > 0;
+       p_test++)
+    {
+      switch (p_test->type)
+        {
+        case TT_TEST:
+          if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
+#ifdef DEBUG
+            if (VLEVEL( VERB_EVERYTHING ))
+              fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
+                       pz_fname, p_fixd->test_ct - test_ct);
+#endif
+            return BOOL_FALSE;
+          }
+          break;
+
+        case TT_EGREP:
+          if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
+#ifdef DEBUG
+            if (VLEVEL( VERB_EVERYTHING ))
+              fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
+                       pz_fname, p_fixd->test_ct - test_ct);
+#endif
+            return BOOL_FALSE;
+          }
+          break;
+
+        case TT_NEGREP:
+          if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
+#ifdef DEBUG
+            if (VLEVEL( VERB_EVERYTHING ))
+              fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
+                       pz_fname, p_fixd->test_ct - test_ct);
+#endif
+            /*  Negated sense  */
+            return BOOL_FALSE;
+          }
+          break;
+
+        case TT_FUNCTION:
+          if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
+              != APPLY_FIX) {
+#ifdef DEBUG
+            if (VLEVEL( VERB_EVERYTHING ))
+              fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
+                       pz_fname, p_fixd->test_ct - test_ct);
+#endif
+            return BOOL_FALSE;
+          }
+          break;
+        }
+    }
+
+  return BOOL_TRUE;
+}
+
+
+/* * * * * * * * * * * * *
+
+   Write out a replacement file  */
+
+static void
+write_replacement (tFixDesc* p_fixd)
+{
+   const char* pz_text = p_fixd->patch_args[0];
+
+   if ((pz_text == (char*)NULL) || (*pz_text == NUL))
+     return;
+
+   {
+     FILE* out_fp = create_file ();
+     size_t sz = strlen (pz_text);
+     fwrite (pz_text, sz, 1, out_fp);
+     if (pz_text[ sz-1 ] != '\n')
+       fputc ('\n', out_fp);
+     fclose (out_fp);
+   }
+}
+
+
+/* * * * * * * * * * * * *
+
+    We have work to do.  Read back in the output
+    of the filtering chain.  Compare each byte as we read it with
+    the contents of the original file.  As soon as we find any
+    difference, we will create the output file, write out all
+    the matched text and then copy any remaining data from the
+    output of the filter chain.
+    */
+static void
+test_for_changes (int read_fd)
+{
+  FILE *in_fp = fdopen (read_fd, "r");
+  FILE *out_fp = (FILE *) NULL;
+  unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
+
+#ifdef DO_STATS
+  fixed_ct++;
+#endif
+  for (;;)
+    {
+      int ch;
+
+      ch = getc (in_fp);
+      if (ch == EOF)
+        break;
+      ch &= 0xFF; /* all bytes are 8 bits */
+
+      /*  IF we are emitting the output
+          THEN emit this character, too.
+      */
+      if (out_fp != (FILE *) NULL)
+        putc (ch, out_fp);
+
+      /*  ELSE if this character does not match the original,
+          THEN now is the time to start the output.
+      */
+      else if (ch != *pz_cmp)
+        {
+          out_fp = create_file ();
+
+#ifdef DO_STATS
+          altered_ct++;
+#endif
+          /*  IF there are matched data, write the matched part now. */
+          if ((char*)pz_cmp != pz_curr_data)
+            fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
+                                       1, out_fp);
+
+          /*  Emit the current unmatching character */
+          putc (ch, out_fp);
+        }
+      else
+        /*  ELSE the character matches.  Advance the compare ptr */
+        pz_cmp++;
+    }
+
+  /*  IF we created the output file, ... */
+  if (out_fp != (FILE *) NULL)
+    {
+      regmatch_t match;
+
+      /* Close the file and see if we have to worry about
+         `#include "file.h"' constructs.  */
+      fclose (out_fp);
+      if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
+        extract_quoted_files (pz_curr_data, pz_curr_file, &match);
+    }
+
+  fclose (in_fp);
+  close (read_fd);  /* probably redundant, but I'm paranoid */
+}
+
+
+/* * * * * * * * * * * * *
+
+   Process the potential fixes for a particular include file.
+   Input:  the original text of the file and the file's name
+   Result: none.  A new file may or may not be created.  */
+
+void
+process (void)
+{
+  tFixDesc *p_fixd = fixDescList;
+  int todo_ct = FIX_COUNT;
+  int read_fd = -1;
+# ifndef SEPARATE_FIX_PROC
+  int num_children = 0;
+# else /* is SEPARATE_FIX_PROC */
+  char* pz_file_source = pz_curr_file;
+# endif
+
+  if (access (pz_curr_file, R_OK) != 0)
+    {
+      int erno = errno;
+      fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
+               pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
+               erno, xstrerror (erno));
+      return;
+    }
+
+  pz_curr_data = load_file (pz_curr_file);
+  if (pz_curr_data == (char *) NULL)
+    return;
+
+#ifdef DO_STATS
+  process_ct++;
+#endif
+  if (VLEVEL( VERB_PROGRESS ) && have_tty)
+    fprintf (stderr, "%6lu %-50s   \r",
+            (unsigned long) data_map_size, pz_curr_file);
+
+# ifndef SEPARATE_FIX_PROC
+  process_chain_head = NOPROCESS;
+
+  /* For every fix in our fix list, ...  */
+  for (; todo_ct > 0; p_fixd++, todo_ct--)
+    {
+      if (! fix_applies (p_fixd))
+        continue;
+
+      if (VLEVEL( VERB_APPLIES ))
+        fprintf (stderr, "Applying %-24s to %s\n",
+                 p_fixd->fix_name, pz_curr_file);
+
+      if (p_fixd->fd_flags & FD_REPLACEMENT)
+        {
+          write_replacement (p_fixd);
+          UNLOAD_DATA();
+          return;
+        }
+
+      /*  IF we do not have a read pointer,
+          THEN this is the first fix for the current file.
+          Open the source file.  That will be used as stdin for
+          the first fix.  Any subsequent fixes will use the
+          stdout descriptor of the previous fix for its stdin.  */
+
+      if (read_fd == -1)
+        {
+          read_fd = open (pz_curr_file, O_RDONLY);
+          if (read_fd < 0)
+            {
+              fprintf (stderr, "Error %d (%s) opening %s\n", errno,
+                       xstrerror (errno), pz_curr_file);
+              exit (EXIT_FAILURE);
+            }
+
+          /*  Ensure we do not get duplicate output */
+
+          fflush (stdout);
+        }
+
+      read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
+      num_children++;
+    }
+
+  /*  IF we have a read-back file descriptor,
+      THEN check for changes and write output if changed.   */
+
+  if (read_fd >= 0)
+    {
+      test_for_changes (read_fd);
+#ifdef DO_STATS
+      apply_ct += num_children;
+#endif
+      /* Wait for child processes created by chain_open()
+         to avoid leaving zombies.  */
+      do  {
+        wait ((int *) NULL);
+      } while (--num_children > 0);
+    }
+
+# else /* is SEPARATE_FIX_PROC */
+
+  for (; todo_ct > 0; p_fixd++, todo_ct--)
+    {
+      if (! fix_applies (p_fixd))
+        continue;
+
+      if (VLEVEL( VERB_APPLIES ))
+        fprintf (stderr, "Applying %-24s to %s\n",
+                 p_fixd->fix_name, pz_curr_file);
+
+      if (p_fixd->fd_flags & FD_REPLACEMENT)
+        {
+          write_replacement (p_fixd);
+          UNLOAD_DATA();
+          return;
+        }
+      fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
+      pz_file_source = pz_temp_file;
+    }
+
+  read_fd = open (pz_temp_file, O_RDONLY);
+  if (read_fd < 0)
+    {
+      if (errno != ENOENT)
+        fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
+                 errno, xstrerror (errno), pz_temp_file);
+    }
+  else
+    {
+      test_for_changes (read_fd);
+      /* Unlinking a file while it is still open is a Bad Idea on
+         DOS/Windows.  */
+      close (read_fd);
+      unlink (pz_temp_file);
+    }
+
+# endif
+  UNLOAD_DATA();
+}