OSDN Git Service

2000-07-21 Alexandre Petit-Bianco <apbianco@cygnus.com>
[pf3gnuchains/gcc-fork.git] / gcc / fixinc / fixincl.c
index f4d76bb..8deafa8 100644 (file)
@@ -3,7 +3,7 @@
    files which are fixed to work correctly with ANSI C and placed in a
    directory that GNU C will search.
 
-   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -22,128 +22,82 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
-#include "auto-host.h"
+#include "fixlib.h"
 
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
+#if HAVE_MMAP_FILE
+#include <sys/mman.h>
+#define  BAD_ADDR ((void*)-1)
 #endif
+
 #include <signal.h>
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#include <ctype.h>
 
-#include "gnu-regex.h"
 #include "server.h"
 
-static const char program_id[] = "fixincl version 1.0";
-
-#define MINIMUM_MAXIMUM_LINES   128
-
-/* If this particular system's header files define the macro `MAXPATHLEN',
-   we happily take advantage of it; otherwise we use a value which ought
-   to be large enough.  */
-#ifndef MAXPATHLEN
-# define MAXPATHLEN     4096
-#endif
-#define NAME_TABLE_SIZE (MINIMUM_MAXIMUM_LINES * MAXPATHLEN)
-
-char *file_name_buf;
+/*  The contents of this string are not very important.  It is mostly
+    just used as part of the "I am alive and working" test.  */
 
-#define tSCC static const char
-#define tCC  const char
-#define tSC  static char
+static const char program_id[] = "fixincl version 1.1";
 
-typedef int t_success;
+/*  This format will be used at the start of every generated file */
 
-#define FAILURE         (-1)
-#define SUCCESS           0
-#define PROBLEM           1
-
-#define SUCCEEDED(p)    ((p) == SUCCESS)
-#define SUCCESSFUL(p)   SUCCEEDED (p)
-#define FAILED(p)       ((p) < SUCCESS)
-#define HADGLITCH(p)    ((p) > SUCCESS)
-
-#define NUL             '\0'
-
-/*  Test Descriptor
-
-    Each fix may have associated tests that determine
-    whether the fix needs to be applied or not.
-    Each test has a type (from the te_test_type enumeration);
-    associated test text; and, if the test is TT_EGREP or
-    the negated form TT_NEGREP, a pointer to the compiled
-    version of the text string.
-
-    */
-typedef enum
-{
-  TT_TEST, TT_EGREP, TT_NEGREP
-} te_test_type;
+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";
 
-typedef struct test_desc tTestDesc;
+/*  Working environment strings.  Essentially, invocation 'options'.  */
 
-struct test_desc
-{
-  te_test_type type;
-  const char *pz_test_text;
-  regex_t *p_test_regex;
-};
+#define _ENV_(v,m,n,t)   tCC* v = NULL;
+ENV_TABLE
+#undef _ENV_
 
-typedef struct patch_desc tPatchDesc;
+int find_base_len = 0;
 
-/*  Fix Descriptor
+typedef enum {
+  VERB_SILENT = 0,
+  VERB_FIXES,
+  VERB_APPLIES,
+  VERB_PROGRESS,
+  VERB_TESTS,
+  VERB_EVERYTHING
+} te_verbose;
 
-    Everything you ever wanted to know about how to apply
-    a particular fix (which files, how to qualify them,
-    how to actually make the fix, etc...)
+te_verbose  verbose_level = VERB_PROGRESS;
+int have_tty = 0;
 
-    */
-#define FD_MACH_ONLY      0x0000
-#define FD_MACH_IFNOT     0x0001
-#define FD_SKIP_TEST      0x8000
+#define VLEVEL(l)  (verbose_level >= l)
+#define NOT_SILENT VLEVEL(VERB_FIXES)
 
-typedef struct fix_desc tFixDesc;
-struct fix_desc
-{
-  const char*   fix_name;       /* Name of the fix */
-  const char*   file_list;      /* List of files it applies to */
-  const char**  papz_machs;     /* List of machine/os-es it applies to */
-  regex_t*      unused;
-  int           test_ct;
-  int           fd_flags;
-  tTestDesc*    p_test_desc;
-  const char**  patch_args;
-};
+pid_t process_chain_head = (pid_t) -1;
 
-/*  Working environment strings.  Essentially, invocation 'options'.  */
-char *pz_dest_dir = NULL;
-char *pz_src_dir = NULL;
-char *pz_machine = NULL;
-char *pz_find_base = NULL;
-int find_base_len = 0;
+char*  pz_curr_file;  /*  name of the current file under test/fix  */
+char*  pz_curr_data;  /*  original contents of that file  */
+t_bool curr_data_mapped;
+int    data_map_fd;
+size_t data_map_size;
+size_t ttl_data_size = 0;
 
-pid_t process_chain_head = (pid_t) -1;
+#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;
 
+void do_version ();
 char *load_file  _P_((const char *));
 void process  _P_((char *, const char *));
 void run_compiles ();
-void wait_for_pid _P_(( pid_t ));
-void initialize ();
+void initialize _P_((int argc,char** argv));
+void process ();
+
+/*  External Source Code */
 
 #include "fixincl.x"
 
@@ -156,204 +110,177 @@ main (argc, argv)
      int argc;
      char **argv;
 {
-  static const char gnu_lib_mark[] =
-    "This file is part of the GNU C Library";
+  char *file_name_buf;
 
-#ifndef NO_BOGOSITY_LIMITS
-# define BOGUS_LIMIT    MINIMUM_MAXIMUM_LINES
-  size_t loop_ct;
-#endif
+  initialize ( argc, argv );
 
-  char *apz_names[BOGUS_LIMIT];
-  size_t file_name_ct;
+  have_tty = isatty (fileno (stderr));
 
   /* Before anything else, ensure we can allocate our file name buffer. */
-  file_name_buf = (char *) malloc (NAME_TABLE_SIZE);
-  if (file_name_buf == (char *) NULL)
-    {
-      fprintf (stderr, "fixincl cannot allocate 0x%08X bytes\n",
-               NAME_TABLE_SIZE);
-      exit (EXIT_FAILURE);
-    }
-
-  switch (argc)
-    {
-    case 1:
-      break;
+  file_name_buf = load_file_data (stdin);
 
-    case 2:
-      if (strcmp (argv[1], "-v") == 0)
-        {
-          static const char zFmt[] = "echo '%s'";
+  /*  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  */
 
-          /* The 'version' option is really used to test that:
-               1.  The program loads correctly (no missing libraries)
-               2.  we can correctly run our server shell process
-               3.  that we can compile all the regular expressions.
-           */
-          run_compiles ();
-          sprintf (file_name_buf, zFmt, program_id);
-          fputs (file_name_buf + 5, stdout);
-          exit (strcmp (run_shell (file_name_buf), program_id));
-        }
-      freopen (argv[1], "r", stdin);
-      break;
+  freopen ("/dev/null", "r", stdin);
 
-    default:
-      fputs ("fixincl ERROR:  too many command line arguments\n", stderr);
+  if (file_name_buf == (char *) NULL)
+    {
+      fputs ("No file names listed for fixing\n", stderr);
       exit (EXIT_FAILURE);
     }
 
-  initialize ();
-
-#ifndef NO_BOGOSITY_LIMITS
-  /*  Some systems only allow so many calls to fork(2).
-      This is inadequate for this program.  Consequently,
-      we must let a grandfather process spawn children
-      that then spawn all the processes that do the real work.
-      */
   for (;;)
     {
-      file_name_ct = 0;
+      char* pz_end;
 
-      {
-        char *pz_buf = file_name_buf;
+      /*  skip to start of name, past any "./" prefixes */
 
-        /* Only the parent process can read from stdin without confusing
-           the world. (How does the child tell the parent to skip
-           forward?  Pipes and files behave differently.)  */
+      while (ISSPACE (*file_name_buf))  file_name_buf++;
+      while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
+        file_name_buf += 2;
 
-        while (  (file_name_ct < BOGUS_LIMIT)
-              && (pz_buf < (file_name_buf + NAME_TABLE_SIZE - MAXPATHLEN)))
-          {
-            if (fgets (pz_buf, MAXPATHLEN, stdin) == (char *) NULL)
-              break;
-            while (isspace (*pz_buf))
-              pz_buf++;
-            if ((*pz_buf == '\0') || (*pz_buf == '#'))
-              continue;
-            apz_names[file_name_ct++] = pz_buf;
-            pz_buf += strlen (pz_buf);
-            while (isspace (pz_buf[-1]))
-              pz_buf--;
-            *pz_buf++ = '\0';
-          }
-      }
-
-      /*  IF we did not get any files this time thru
-          THEN we must be done.  */
-      if (file_name_ct == 0)
-        return EXIT_SUCCESS;
+      /*  Check for end of list  */
 
-      fflush (stdout);
-      fflush (stderr);
-
-      {
-        pid_t child = fork ();
-        if (child == NULLPROCESS)
-          break;
+      if (*file_name_buf == NUL)
+        break;
 
-        if (child == NOPROCESS)
-          {
-            fprintf (stderr, "Error %d (%s) forking in main\n",
-                     errno, strerror (errno));
-            exit (EXIT_FAILURE);
-          }
+      /*  Set global file name pointer and find end of name */
 
-#ifdef DEBUG
-        fprintf (stderr, "Waiting for %d to complete %d files\n",
-                 child, file_name_ct);
-#endif
+      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;
 
-        wait_for_pid( child, file_name_ct );
-      }
-    }
-#else
-#error "NON-BOGUS LIMITS NOT SUPPORTED?!?!"
-#endif
+      while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1]))  pz_end--;
 
-  /*
-     Here we are the child of the grandparent process.  The parent
-     of all the little fixup processes.  We ignore the deaths of
-     our children.  */
+      /*  IF no name is found (blank line) or comment marker, skip line  */
 
-  signal (SIGCLD,  SIG_IGN);
+      if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
+        continue;
+      *pz_end = NUL;
 
-  /*  For every file specified in stdandard in
-      (except as throttled for bogus reasons)...
-      */
-  for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++)
-    {
-      char *pz_data;
-      char *pz_file_name = apz_names[loop_ct];
+      process ();
+    } /*  for (;;) */
 
-      if (access (pz_file_name, R_OK) != 0)
-        {
-          int erno = errno;
-          fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
-                   pz_file_name, getcwd ((char *) NULL, MAXPATHLEN),
-                   erno, strerror (erno));
-        }
-      else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL))
-        {
-          if (strstr (pz_data, gnu_lib_mark) == (char *) NULL)
-            process (pz_data, pz_file_name);
-          free ((void *) pz_data);
-        }
-    }
+#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 */
   return EXIT_SUCCESS;
 }
 
 
+void
+do_version ()
+{
+  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);
+  puts (zBuf + 5);
+  exit (strcmp (run_shell (zBuf), program_id));
+}
+
 /* * * * * * * * * * * * */
 
 void
-initialize()
+initialize ( argc, argv )
+  int argc;
+  char** argv;
 {
   static const char var_not_found[] =
-    "fixincl ERROR:  %s environment variable not defined\n";
+    "fixincl ERROR:  %s environment variable not defined\n"
+#ifdef __STDC__
+    "each of these must be defined:\n"
+#define _ENV_(v,m,n,t) "\t" n "  - " t "\n"
+ENV_TABLE
+#undef _ENV_
+#endif
+    ;
 
-  {
-    static const char var[] = "TARGET_MACHINE";
-    pz_machine = getenv (var);
-    if (pz_machine == (char *) NULL)
-      {
-        fprintf (stderr, var_not_found, var);
-        exit (EXIT_FAILURE);
-      }
-  }
+  xmalloc_set_program_name (argv[0]);
 
-  {
-    static const char var[] = "DESTDIR";
-    pz_dest_dir = getenv (var);
-    if (pz_dest_dir == (char *) NULL)
-      {
-        fprintf (stderr, var_not_found, var);
-        exit (EXIT_FAILURE);
-      }
-  }
+  switch (argc)
+    {
+    case 1:
+      break;
 
-  {
-    static const char var[] = "SRCDIR";
-    pz_src_dir = getenv (var);
-    if (pz_src_dir == (char *) NULL)
-      {
-        fprintf (stderr, var_not_found, var);
-        exit (EXIT_FAILURE);
-      }
-  }
+    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;
 
-  {
-    static const char var[] = "FIND_BASE";
-    pz_find_base = getenv (var);
-    if (pz_find_base == (char *) NULL)
-      {
-        fprintf (stderr, var_not_found, var);
-        exit (EXIT_FAILURE);
-      }
-    find_base_len = strlen( pz_find_base );
-  }
+    default:
+      fputs ("fixincl ERROR:  too many command line arguments\n", stderr);
+      exit (EXIT_FAILURE);
+    }
+
+#define _ENV_(v,m,n,t)   { tSCC var[] = n;  \
+  v = getenv (var); if (m && (v == NULL)) { \
+  fprintf (stderr, var_not_found, var);     \
+  exit (EXIT_FAILURE); } }
+
+ENV_TABLE
+
+#undef _ENV_
+
+  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;
+
+    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;
+    }
+
+ 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.
@@ -361,168 +288,78 @@ initialize()
   run_compiles ();
 
   signal (SIGQUIT, SIG_IGN);
+#ifdef SIGIOT
   signal (SIGIOT,  SIG_IGN);
+#endif
   signal (SIGPIPE, SIG_IGN);
   signal (SIGALRM, SIG_IGN);
   signal (SIGTERM, SIG_IGN);
-
-  /*
-     Make sure that if we opened a server process, we close it now.
-     This is the grandparent process.  We don't need the server anymore
-     and our children should make their own.  */
-
-  close_server ();
-  (void)wait ( (int*)NULL );
 }
 
 /* * * * * * * * * * * * *
-   wait_for_pid  -  Keep calling `wait(2)' until it returns
-   the process id we are looking for.  Not every system has
-   `waitpid(2)'.  We also ensure that the children exit with success. */
-
-void
-wait_for_pid( pid_t child, int file_name_ct )
-{
-  for (;;) {
-    int status;
-    pid_t dead_kid = wait (&status);
-
-    if (dead_kid == child)
-      {
-        if (! WIFEXITED( status ))
-          {
-            fprintf (stderr, "child process %d is hung on signal %d\n",
-                     child, WSTOPSIG( status ));
-            exit (EXIT_FAILURE);
-          }
-        if (WEXITSTATUS( status ) != 0)
-          {
-            fprintf (stderr, "child process %d exited with status %d\n",
-                     child, WEXITSTATUS( status ));
-            exit (EXIT_FAILURE);
-          }
-        break; /* normal child completion */
-      }
 
-    /*
-       IF there is an error, THEN see if it is retryable.
-       If it is not retryable, then break out of this loop.  */
-    if (dead_kid == NOPROCESS)
-      {
-        switch (errno) {
-        case EINTR:
-        case EAGAIN:
-          break;
-
-        default:
-          fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
-                   errno, strerror( errno ), child );
-          /* FALLTHROUGH */
-
-        case ECHILD: /* no children to wait for?? */
-          return;
-        }
-      }
-  } done_waiting:;
-}
-
-
-/* * * * * * * * * * * * *
    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 (pz_file_name)
-     const char *pz_file_name;
+load_file ( fname )
+    const char* fname;
 {
-  char *pz_data;
-  size_t file_size;
+  struct stat stbf;
+  char* res;
 
-  {
-    struct stat stbf;
-    
-    if (stat (pz_file_name, &stbf) != 0)
-      {
-        fprintf (stderr, "error %d (%s) stat-ing %s\n",
-                 errno, strerror (errno), pz_file_name);
-        return (char *) NULL;
-      }
-    file_size = stbf.st_size;
-  }
-  if (file_size == 0)
-    return (char *) NULL;
-
-  pz_data = (char *) malloc ((file_size + 16) & ~0x00F);
-  if (pz_data == (char *) NULL)
+  if (stat (fname, &stbf) != 0)
     {
-      fprintf (stderr, "error:  could not malloc %d bytes\n",
-               file_size);
-      exit (EXIT_FAILURE);
+      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;
 
-  {
-    FILE *fp = fopen (pz_file_name, "r");
-    size_t size_left = file_size;
-    char *read_ptr = pz_data;
+  data_map_size = stbf.st_size+1;
+  data_map_fd   = open (fname, O_RDONLY);
+  ttl_data_size += data_map_size-1;
 
-    if (fp == (FILE *) NULL)
-      {
-        fprintf (stderr, "error %d (%s) opening %s\n", errno,
-                 strerror (errno), pz_file_name);
-        free ((void *) pz_data);
-        return (char *) NULL;
-      }
-
-    do
-      {
-        size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp);
-
-        if (sizeRead == 0)
-          {
-            if (feof (fp))
-              break;
-
-            if (ferror (fp))
-              {
-                int err = errno;
-                if (err != EISDIR)
-                  fprintf (stderr, "error %d (%s) reading %s\n", err,
-                           strerror (err), pz_file_name);
-                free ((void *) pz_data);
-                fclose (fp);
-                return (char *) NULL;
-              }
-          }
+  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;
+    }
 
-        read_ptr += sizeRead;
-        size_left -= sizeRead;
-      }
-    while (size_left != 0);
+#ifdef HAVE_MMAP_FILE
+  curr_data_mapped = BOOL_TRUE;
+  res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ, MAP_PRIVATE,
+                     data_map_fd, 0);
+  if (res == (char*)BAD_ADDR)
+    {
+      curr_data_mapped = BOOL_FALSE;
+      res = load_file_data ( fdopen (data_map_fd, "r"));
+    }
+#else
+  curr_data_mapped = BOOL_FALSE;
+  res = load_file_data ( fdopen (data_map_fd, "r"));
+#endif
 
-    *read_ptr = '\0';
-    fclose (fp);
-  }
-  return pz_data;
+  return res;
 }
 
 
 /* * * * * * * * * * * * *
+
    run_compiles   run all the regexp compiles for all the fixes once.
  */
 void
 run_compiles ()
 {
-  tSCC z_bad_comp[] = "fixincl ERROR:  cannot compile %s regex for %s\n\
-\texpr = `%s'\n\terror %s\n";
   tFixDesc *p_fixd = fixDescList;
   int fix_ct = FIX_COUNT;
   tTestDesc *p_test;
   int test_ct;
-  int re_ct = REGEX_COUNT;
   const char *pz_err;
   regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
 
@@ -533,26 +370,18 @@ run_compiles ()
       exit (EXIT_FAILURE);
     }
 
-  /*  Make sure re_compile_pattern does not stumble across invalid
-      data */
+  /*  Make sure compile_re does not stumble across invalid data */
 
   memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
   memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
 
-  /*  The patterns we search for are all egrep patterns.
-      In the shell version of this program, we invoke egrep
-      with the supplied pattern.  Here, we will run
-      re_compile_pattern, but it must be using the same rules.  */
+  compile_re (incl_quote_pat, &incl_quote_re, 1,
+              "quoted include", "run_compiles");
 
-  re_set_syntax (RE_SYNTAX_EGREP);
-  pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
-                              &incl_quote_re);
-  if (pz_err != (char *) NULL)
-    {
-      fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
-               incl_quote_pat, pz_err);
-      exit (EXIT_FAILURE);
-    }
+  /*  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
@@ -567,28 +396,26 @@ run_compiles ()
       if (  (pz_machine != NULL)
          && (p_fixd->papz_machs != (const char**) NULL) )
         {
+          tSCC case_fmt[] = "case %s in\n";     /*  9 bytes, plus string */
+          tSCC esac_fmt[] =
+               " )\n    echo %s ;;\n* ) echo %s ;;\nesac";/*  4 bytes */
+          tSCC skip[] = "skip";                 /*  4 bytes */
+          tSCC run[] = "run";                   /*  3 bytes */
+          /* total bytes to add to machine sum:    49 - see fixincl.tpl */
+
           const char **papz_machs = p_fixd->papz_machs;
-          char *pz = file_name_buf;
+          char *pz;
           char *pz_sep = "";
           tCC *pz_if_true;
           tCC *pz_if_false;
-          tSCC skip[] = "skip";
-          tSCC run[] = "run";
-
-          /*  Construct a shell script that looks like this:
+          char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
 
-              case our-cpu-platform-os in
-              tests-cpu-platform-os-pattern )
-                  echo run ;;
-              * )
-                  echo skip ;;
-              esac
+          /* Start the case statement */
 
-              where 'run' and 'skip' may be reversed, depending on
-              the sense of the test.  */
+          sprintf (cmd_buf, case_fmt, pz_machine);
+          pz = cmd_buf + strlen (cmd_buf);
 
-          sprintf (pz, "case %s in\n", pz_machine);
-          pz += strlen (pz);
+          /*  Determine if a match means to apply the fix or not apply it */
 
           if (p_fixd->fd_flags & FD_MACH_IFNOT)
             {
@@ -601,8 +428,8 @@ run_compiles ()
               pz_if_false = skip;
             }
 
-          /*  FOR any additional machine names to test for,
-              insert the " | \\\n" glue and the machine pattern.  */
+          /*  Emit all the machine names.  If there are more than one,
+              then we will insert " | \\\n" between the names  */
 
           for (;;)
             {
@@ -610,19 +437,21 @@ run_compiles ()
 
               if (pz_mach == (const char*) NULL)
                 break;
-              sprintf (pz, "%s  %s", pz_sep, pz_mach);
+              sprintf (pz, "%s%s", pz_sep, pz_mach);
               pz += strlen (pz);
               pz_sep = " | \\\n";
             }
-          sprintf (pz, " )\n    echo %s ;;\n  * )\n    echo %s ;;\nesac",
-                   pz_if_true, pz_if_false);
+
+          /* Now emit the match and not-match actions and the esac */
+
+          sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
 
           /*  Run the script.
               The result will start either with 's' or 'r'.  */
 
           {
             int skip;
-            pz = run_shell (file_name_buf);
+            pz = run_shell (cmd_buf);
             skip = (*pz == 's');
             free ( (void*)pz );
             if (skip)
@@ -641,26 +470,20 @@ run_compiles ()
             {
             case TT_EGREP:
             case TT_NEGREP:
-              /*  You might consider putting the following under #ifdef.
-                  The number of re's used is computed by autogen.
-                  So, it is static and known at compile time.  */
-
-              if (--re_ct < 0)
-                {
-                  fputs ("out of RE's\n", stderr);
-                  exit (EXIT_FAILURE);
-                }
+#ifdef DEBUG
+              {
+                static int re_ct = REGEX_COUNT;
 
+                if (--re_ct < 0)
+                  {
+                    fputs ("out of RE's\n", stderr);
+                    exit (EXIT_FAILURE);
+                  }
+              }
+#endif
               p_test->p_test_regex = p_re++;
-              pz_err = re_compile_pattern (p_test->pz_test_text,
-                                          strlen (p_test->pz_test_text),
-                                          p_test->p_test_regex);
-              if (pz_err != (char *) NULL)
-                {
-                  fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name,
-                           p_test->pz_test_text, pz_err);
-                  exit (EXIT_FAILURE);
-                }
+              compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
+                          "select test", p_fixd->fix_name);
             }
           p_test++;
         }
@@ -670,31 +493,36 @@ run_compiles ()
 
 
 /* * * * * * * * * * * * *
+
    create_file  Create the output modified file.
    Input:    the name of the file to create
    Returns:  a file pointer to the new, open file  */
 
-#define S_IRALL        (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#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
+
 
 FILE *
-create_file (pz_file_name)
-     const char *pz_file_name;
+create_file ()
 {
   int fd;
   FILE *pf;
   char fname[MAXPATHLEN];
 
-#ifdef DEBUG
-  if (strncmp( pz_file_name, pz_find_base, find_base_len ) != 0)
-    {
-      fprintf (stderr, "Error:  input file %s does not match %s/*\n",
-              pz_file_name, pz_find_base );
-      exit (1);
-    }
-#endif
-
-  sprintf (fname, "%s/%s", pz_dest_dir, pz_file_name + find_base_len);
+  sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
 
   fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
 
@@ -709,8 +537,7 @@ create_file (pz_file_name)
           *pz_dir = NUL;
           if (stat (fname, &stbf) < 0)
             {
-              mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
-                     | S_IROTH | S_IXOTH);
+              mkdir (fname, S_IFDIR | S_DIRALL);
             }
 
           *pz_dir = '/';
@@ -723,23 +550,23 @@ create_file (pz_file_name)
   if (fd < 0)
     {
       fprintf (stderr, "Error %d (%s) creating %s\n",
-               errno, strerror (errno), fname);
+               errno, xstrerror (errno), fname);
       exit (EXIT_FAILURE);
     }
-  fprintf (stderr, "Fixed:  %s\n", pz_file_name);
+  if (NOT_SILENT)
+    fprintf (stderr, "Fixed:  %s\n", pz_curr_file);
   pf = fdopen (fd, "w");
 
-#ifdef LATER
-  {
-    static const char hdr[] =
-    "/*  DO NOT EDIT THIS FILE.\n\n"
-    "    It has been auto-edited by fixincludes from /usr/include/%s\n"
-    "    This had to be done to correct non-standard usages in the\n"
-    "    original, manufacturer supplied header file.  */\n\n";
+  /*
+   *  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);
 
-    fprintf (pf, hdr, pz_file_name);
-  }
-#endif
   return pf;
 }
 
@@ -749,13 +576,13 @@ create_file (pz_file_name)
   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: SUCCESS or FAILURE, depending on the result of the
+  Result: APPLY_FIX or SKIP_FIX, depending on the result of the
           shell script we run.  */
 
-t_success
-test_test (p_test, pz_file_name)
+int
+test_test (p_test, pz_test_file)
      tTestDesc *p_test;
-     char*      pz_file_name;
+     char*      pz_test_file;
 {
   tSCC cmd_fmt[] =
 "file=%s\n\
@@ -765,44 +592,55 @@ else echo FALSE\n\
 fi";
 
   char *pz_res;
-  t_success res = FAILURE;
+  int res;
 
   static char cmd_buf[4096];
 
-  sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text);
+  sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
   pz_res = run_shell (cmd_buf);
-  if (*pz_res == 'T')
-    res = SUCCESS;
+
+  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 );
+  }
+
   free ((void *) pz_res);
   return res;
 }
 
 
 /* * * * * * * * * * * * *
+
   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: SUCCESS if the pattern is found, FAILURE otherwise
+  Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
 
-  The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test
+  The caller may choose to reverse meaning if the sense of the test
   is inverted.  */
 
-t_success
+int
 egrep_test (pz_data, p_test)
      char *pz_data;
      tTestDesc *p_test;
 {
-  regmatch_t match;
-
-#ifndef NO_BOGOSITY
+#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 (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
-    return SUCCESS;
-  return FAILURE;
+  if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
+    return APPLY_FIX;
+  return SKIP_FIX;
 }
 
 
@@ -825,7 +663,7 @@ quoted_file_exists (pz_src_path, pz_file_path, pz_file)
 
   for (;;) {
     char ch = *pz_file++;
-    if (! isgraph( ch ))
+    if (! ISGRAPH( ch ))
       return 0;
     if (ch == '"')
       break;
@@ -844,7 +682,7 @@ quoted_file_exists (pz_src_path, pz_file_path, pz_file)
 /* * * * * * * * * * * * *
  *
    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
@@ -852,7 +690,7 @@ quoted_file_exists (pz_src_path, pz_file_path, pz_file)
    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
@@ -864,21 +702,22 @@ quoted_file_exists (pz_src_path, pz_file_path, pz_file)
 
 
 void
-extract_quoted_files (pz_data, pz_file_name, p_re_match)
+extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
      char *pz_data;
-     const char *pz_file_name;
+     const char *pz_fixed_file;
      regmatch_t *p_re_match;
 {
-  char *pz_dir_end = strrchr (pz_file_name, '/');
+  char *pz_dir_end = strrchr (pz_fixed_file, '/');
   char *pz_incl_quot = pz_data;
 
-  fprintf (stderr, "Quoted includes in %s\n", pz_file_name);
+  if (VLEVEL( VERB_APPLIES ))
+    fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
 
-  /*  Set "pz_file_name" to point to the containing subdirectory of the source
+  /*  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_file_name = ".";
+    pz_fixed_file = ".";
   else
     *pz_dir_end = '\0';
 
@@ -887,19 +726,20 @@ extract_quoted_files (pz_data, pz_file_name, p_re_match)
       pz_incl_quot += p_re_match->rm_so;
 
       /*  Skip forward to the included file name */
-      while (isspace (*pz_incl_quot))
+      while (ISSPACE (*pz_incl_quot))
         pz_incl_quot++;
-      while (isspace (*++pz_incl_quot))
+      /* ISSPACE() may evaluate is argument more than once!  */
+      while (++pz_incl_quot, ISSPACE (*pz_incl_quot))
         ;
       pz_incl_quot += sizeof ("include") - 1;
       while (*pz_incl_quot++ != '"')
         ;
 
-      if (quoted_file_exists (pz_src_dir, pz_file_name, 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_file_name);
+          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 */
@@ -908,7 +748,7 @@ extract_quoted_files (pz_data, pz_file_name, p_re_match)
 
           /* Now print the destination directory appended with the
              relative path of the desired file */
-          printf ("  %s/%s/", pz_dest_dir, pz_file_name);
+          printf ("  %s/%s/", pz_dest_dir, pz_fixed_file);
           while (*pz_dir_end != '"')
             putc (*pz_dir_end++, stdout);
 
@@ -925,258 +765,458 @@ extract_quoted_files (pz_data, pz_file_name, p_re_match)
 
 /* * * * * * * * * * * * *
 
-   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.  */
+    Somebody wrote a *_fix subroutine that we must call.
+    */
 
-void
-process (pz_data, pz_file_name)
-     char *pz_data;
-     const char *pz_file_name;
+int
+internal_fix (read_fd, p_fixd)
+  int read_fd;
+  tFixDesc* p_fixd;
 {
-  static char env_current_file[1024] = { "file=" };
-  tFixDesc *p_fixd = fixDescList;
-  int todo_ct = FIX_COUNT;
-  t_fd_pair fdp = { -1, -1 };
-  int num_children = 0;
+  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];
+        }
 
-  /*  IF this is the first time through,
-      THEN put the 'file' environment variable into the environment.
-           This is used by some of the subject shell scripts and tests.   */
+      /*
+       *  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:;
 
-  if (env_current_file[5] == NUL)
-    putenv (env_current_file);
+  /*
+   *  Close our current stdin and stdout
+   */
+  close (STDIN_FILENO);
+  close (STDOUT_FILENO);
+  UNLOAD_DATA();
 
   /*
-     Ghastly as it is, this actually updates the value of the variable:
-   
-       putenv(3C)             C Library Functions             putenv(3C)
-   
-       DESCRIPTION
-            putenv() makes the value of the  environment  variable  name
-            equal  to value by altering an existing variable or creating
-            a new one.  In either case, the string pointed to by  string
-            becomes part of the environment, so altering the string will
-            change the environment.  string points to a  string  of  the
-            form  ``name=value.''  The space used by string is no longer
-            used once a new string-defining name is passed to putenv().
+   *  Make the fd passed in the stdin, and the write end of
+   *  the new pipe become the stdout.
    */
-  strcpy (env_current_file + 5, pz_file_name);
-  process_chain_head = NOPROCESS;
-  fprintf (stderr, "%-50s   \r", pz_file_name );
-  /* For every fix in our fix list, ...  */
-  for (; todo_ct > 0; p_fixd++, todo_ct--)
+  fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
+  fcntl (read_fd, F_DUPFD, STDIN_FILENO);
+
+  apply_fix (p_fixd, pz_curr_file);
+  exit (0);
+}
+
+
+/* * * * * * * * * * * * *
+
+    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.  */
+
+int
+start_fixer (read_fd, p_fixd, pz_fix_file)
+  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 = (char*)NULL;
+  else
     {
-      tTestDesc *p_test;
-      int test_ct;
+      tSCC z_cmd_fmt[] = "file='%s'\n%s";
+      pz_cmd = (char*)malloc (strlen (p_fixd->patch_args[2])
+                               + sizeof( z_cmd_fmt )
+                               + strlen( pz_fix_file ));
+      if (pz_cmd == (char*)NULL)
+        {
+          fputs ("allocation failure\n", stderr);
+          exit (EXIT_FAILURE);
+        }
+      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;
+    }
 
-      if (p_fixd->fd_flags & FD_SKIP_TEST)
-        continue;
+  /*  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.
 
-      /*  IF there is a file name restriction,
-          THEN ensure the current file name matches one in the pattern  */
+      */
+  for (;;)
+    {
+      static int failCt = 0;
+      int fd;
 
-      if (p_fixd->file_list != (char *) NULL)
+      fd = chain_open (read_fd,
+                       (t_pchar *) p_fixd->patch_args,
+                       (process_chain_head == -1)
+                       ? &process_chain_head : (pid_t *) NULL);
+
+      if (fd != -1)
         {
-          const char *pz_fname = pz_file_name;
-          const char *pz_scan = p_fixd->file_list;
-          size_t name_len;
+          read_fd = fd;
+          break;
+        }
 
-          while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
-            pz_fname += 2;
-          name_len = strlen (pz_fname);
+      fprintf (stderr, z_fork_err, errno, xstrerror (errno),
+               p_fixd->fix_name);
 
-          for (;;)
-            {
-              pz_scan = strstr (pz_scan + 1, pz_fname);
-              /*  IF we can't match the string at all,
-                  THEN bail  */
-              if (pz_scan == (char *) NULL)
-                goto next_fix;
+      if ((errno != EAGAIN) || (++failCt > 10))
+        exit (EXIT_FAILURE);
+      sleep (1);
+    }
 
-              /*  IF the match is surrounded by the '|' markers,
-                  THEN we found a full match -- time to run the tests  */
+  /*  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;
+    }
 
-              if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
-                break;
-            }
+  return read_fd;
+}
+
+
+/* * * * * * * * * * * * *
+
+   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.  */
+
+t_bool
+fix_applies (p_fixd)
+  tFixDesc *p_fixd;
+{
+#ifdef DEBUG
+  static const char z_failed[] = "not applying %s %s to %s - \
+test %d failed\n";
+#endif
+  const char *pz_fname = pz_curr_file;
+  const char *pz_scan = p_fixd->file_list;
+  int test_ct;
+  tTestDesc *p_test;
+
+  if (p_fixd->fd_flags & FD_SKIP_TEST)
+    return BOOL_FALSE;
+
+  /*  IF there is a file name restriction,
+      THEN ensure the current file name matches one in the pattern  */
+
+  if (pz_scan != (char *) NULL)
+    {
+      size_t name_len;
+
+      while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
+        pz_fname += 2;
+      name_len = strlen (pz_fname);
+
+      for (;;)
+        {
+          pz_scan = strstr (pz_scan + 1, pz_fname);
+          /*  IF we can't match the string at all,
+              THEN bail  */
+          if (pz_scan == (char *) NULL) {
+#ifdef DEBUG
+            if (VLEVEL( VERB_EVERYTHING ))
+              fprintf (stderr, "file %s not in list for %s\n",
+                       pz_fname, p_fixd->fix_name );
+#endif
+            return BOOL_FALSE;
+          }
+
+          /*  IF the match is surrounded by the '|' markers,
+              THEN we found a full match -- time to run the tests  */
+
+          if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
+            break;
         }
+    }
 
-      /*  FOR each test, see if it fails.
-          IF it does fail, then we go on to the next test */
+  /*  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++)
+  for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
+       test_ct-- > 0;
+       p_test++)
+    {
+      switch (p_test->type)
         {
-#ifdef DEBUG_TEST
-          static const char z_test_fail[] =
-            "%16s test %2d failed for %s\n";
+        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
-          switch (p_test->type)
-            {
-            case TT_TEST:
-              if (!SUCCESSFUL (test_test (p_test, pz_file_name)))
-                {
-#ifdef DEBUG_TEST
-                  fprintf (stderr, z_test_fail, p_fixd->fix_name,
-                           p_fixd->test_ct - test_ct, pz_file_name);
+            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
-                  goto next_fix;
-                }
-              break;
+            return BOOL_FALSE;
+          }
+          break;
 
-            case TT_EGREP:
-              if (!SUCCESSFUL (egrep_test (pz_data, p_test)))
-                {
-#ifdef DEBUG_TEST
-                  fprintf (stderr, z_test_fail, p_fixd->fix_name,
-                           p_fixd->test_ct - test_ct, pz_file_name);
+        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
-                  goto next_fix;
-                }
-              break;
+            /*  Negated sense  */
+            return BOOL_FALSE;
+          }
+          break;
 
-            case TT_NEGREP:
-              if (SUCCESSFUL (egrep_test (pz_data, p_test)))
-                {
-#ifdef DEBUG_TEST
-                  fprintf (stderr, z_test_fail, p_fixd->fix_name,
-                           p_fixd->test_ct - test_ct, pz_file_name);
+        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
-                  goto next_fix;
-                }
-              break;
-            }
+            return BOOL_FALSE;
+          }
+          break;
         }
+    }
 
-      fprintf (stderr, "Applying %-24s to %s\n",
-               p_fixd->fix_name, pz_file_name);
+  return BOOL_TRUE;
+}
 
-      /*  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 as its stdin.  */
 
-      if (fdp.read_fd == -1)
-        {
-          fdp.read_fd = open (pz_file_name, O_RDONLY);
-          if (fdp.read_fd < 0)
-            {
-              fprintf (stderr, "Error %d (%s) opening %s\n", errno,
-                       strerror (errno), pz_file_name);
-              exit (EXIT_FAILURE);
-            }
-        }
+/* * * * * * * * * * * * *
 
-      /*  This loop should only cycle for 1/2 of one loop.
-          "chain_open" starts a process that uses "fdp.read_fd" as
-          its stdin and returns the new fd this process will use
-          for stdout.  */
+   Write out a replacement file  */
 
-      for (;;)
+void
+write_replacement (p_fixd)
+  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 (pz_curr_file);
+     fputs (pz_text, 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.
+    */
+void
+test_for_changes (read_fd)
+  int read_fd;
+{
+  FILE *in_fp = fdopen (read_fd, "r");
+  FILE *out_fp = (FILE *) NULL;
+  char *pz_cmp = pz_curr_data;
+
+#ifdef DO_STATS
+  fixed_ct++;
+#endif
+  for (;;)
+    {
+      int ch;
+
+      ch = getc (in_fp);
+      if (ch == EOF)
+        break;
+
+      /*  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)
         {
-          tSCC z_err[] = "Error %d (%s) starting filter process for %s\n";
-          static int failCt = 0;
-          int fd = chain_open (fdp.read_fd,
-                               (t_pchar *) p_fixd->patch_args,
-                               (process_chain_head == -1)
-                               ? &process_chain_head : (pid_t *) NULL);
-
-          if (fd != -1)
-            {
-              fdp.read_fd = fd;
-              num_children++;
-              break;
-            }
+          out_fp = create_file (pz_curr_file);
 
-          fprintf (stderr, z_err, errno, strerror (errno),
-                   p_fixd->fix_name);
+#ifdef DO_STATS
+          altered_ct++;
+#endif
+          /*  IF there are matched data, write the matched part now. */
+          if (pz_cmp != pz_curr_data)
+            fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
 
-          if ((errno != EAGAIN) || (++failCt > 10))
-            exit (EXIT_FAILURE);
-          sleep (1);
+          /*  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;
 
-    next_fix:
-      ;
+      /* Close the file and see if we have to worry about
+         `#include "file.h"' constructs.  */
+      fclose (out_fp);
+      if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
+        extract_quoted_files (pz_curr_data, pz_curr_file, &match);
     }
 
-  /*  IF after all the tests we did not start any patch programs,
-      THEN quit now.   */
+  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 ()
+{
+  static char env_current_file[1024];
+  tFixDesc *p_fixd = fixDescList;
+  int todo_ct = FIX_COUNT;
+  int read_fd = -1;
+  int num_children = 0;
+
+  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;
+    }
 
-  if (fdp.read_fd < 0)
+  pz_curr_data = load_file (pz_curr_file);
+  if (pz_curr_data == (char *) NULL)
     return;
 
-  /*  OK.  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.
-      */
-  {
-    FILE *in_fp = fdopen (fdp.read_fd, "r");
-    FILE *out_fp = (FILE *) NULL;
-    char *pz_cmp = pz_data;
+#ifdef DO_STATS
+  process_ct++;
+#endif
+  if (VLEVEL( VERB_PROGRESS ) && have_tty)
+    fprintf (stderr, "%6d %-50s   \r", data_map_size, pz_curr_file );
 
-    for (;;)
-      {
-        int ch;
+  process_chain_head = NOPROCESS;
 
-        ch = getc (in_fp);
-        if (ch == EOF)
-          break;
+  /* For every fix in our fix list, ...  */
+  for (; todo_ct > 0; p_fixd++, todo_ct--)
+    {
+      if (! fix_applies (p_fixd))
+        continue;
 
-        /*  IF we are emitting the output
-            THEN emit this character, too.
-            */
-        if (out_fp != (FILE *) NULL)
-          putc (ch, out_fp);
+      if (VLEVEL( VERB_APPLIES ))
+        fprintf (stderr, "Applying %-24s to %s\n",
+                 p_fixd->fix_name, pz_curr_file);
 
-        /*  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 (pz_file_name);
+      if (p_fixd->fd_flags & FD_REPLACEMENT)
+        {
+          write_replacement (p_fixd);
+          UNLOAD_DATA();
+          return;
+        }
 
-            /*  IF there are matched data, write it all now. */
-            if (pz_cmp != pz_data)
-              {
-                char c = *pz_cmp;
-                
-                *pz_cmp = NUL;
-                fputs (pz_data, out_fp);
-                *pz_cmp = c;
-              }
+      /*  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.  */
 
-            /*  Emit the current unmatching character */
-            putc (ch, out_fp);
-          }
-        else
-          /*  ELSE the character matches.  Advance the compare ptr */
-          pz_cmp++;
-      }
+      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);
+            }
 
-    /*  IF we created the output file, ... */
-    if (out_fp != (FILE *) NULL)
-      {
-        regmatch_t match;
+          /*  Ensure we do not get duplicate output */
 
-        /* Close the file and see if we have to worry about
-          `#include "file.h"' constructs.  */
-        fclose (out_fp);
-        if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0)
-          extract_quoted_files (pz_data, pz_file_name, &match);
-      }
-    fclose (in_fp);
-  }
-  close (fdp.read_fd);  /* probably redundant, but I'm paranoid */
+          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);
+    }
 
-  /* Wait for child processes created by chain_open()
-     to avoid creating zombies.  */
-  while (--num_children >= 0)
-    wait ((int *) NULL);
+  UNLOAD_DATA();
 }