fixincl.c 32.9 KB
Newer Older
1

korbb's avatar
korbb committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* 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 GNU C will search.

   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.

This file is part of GNU CC.

GNU CC 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 2, or (at your option)
any later version.

GNU CC 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

korbb's avatar
korbb committed
25 26
#include "fixlib.h"

27 28 29 30
#if HAVE_MMAP
#include <sys/mman.h>
#define  BAD_ADDR ((void*)-1)
#endif
korbb's avatar
korbb committed
31

32 33 34 35
#include <signal.h>

#include "server.h"

korbb's avatar
korbb committed
36
/*  Quality Assurance Marker  :-)
korbb's avatar
korbb committed
37

korbb's avatar
korbb committed
38 39
    Any file that contains this string is presumed to have
    been carefully constructed and will not be fixed  */
40

korbb's avatar
korbb committed
41 42
static const char gnu_lib_mark[] =
    "This file is part of the GNU C Library";
43

korbb's avatar
korbb committed
44 45
/*  The contents of this string are not very important.  It is mostly
    just used as part of the "I am alive and working" test.  */
46

korbb's avatar
korbb committed
47
static const char program_id[] = "fixincl version 1.1";
48

korbb's avatar
korbb committed
49
/*  Test Descriptor
50

korbb's avatar
korbb committed
51 52 53 54 55 56 57 58
    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.

    */
59
typedef enum
korbb's avatar
korbb committed
60
{
korbb's avatar
korbb committed
61
  TT_TEST, TT_EGREP, TT_NEGREP, TT_FUNCTION
korbb's avatar
korbb committed
62
} te_test_type;
63 64 65 66

typedef struct test_desc tTestDesc;

struct test_desc
korbb's avatar
korbb committed
67 68 69 70 71
{
  te_test_type type;
  const char *pz_test_text;
  regex_t *p_test_regex;
};
72 73 74

typedef struct patch_desc tPatchDesc;

korbb's avatar
korbb committed
75 76 77 78 79 80
/*  Fix Descriptor

    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...)

korbb's avatar
korbb committed
81 82
    NB:  the FD_ defines are BIT FLAGS

korbb's avatar
korbb committed
83
    */
84 85
#define FD_MACH_ONLY      0x0000
#define FD_MACH_IFNOT     0x0001
korbb's avatar
korbb committed
86
#define FD_SHELL_SCRIPT   0x0002
korbb's avatar
korbb committed
87 88
#define FD_SUBROUTINE     0x0004
#define FD_REPLACEMENT    0x0008
89 90 91 92
#define FD_SKIP_TEST      0x8000

typedef struct fix_desc tFixDesc;
struct fix_desc
korbb's avatar
korbb committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
{
  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;
};

/*  Working environment strings.  Essentially, invocation 'options'.  */
char *pz_dest_dir = NULL;
char *pz_src_dir = NULL;
char *pz_machine = NULL;
108
int find_base_len = 0;
korbb's avatar
korbb committed
109 110 111

pid_t process_chain_head = (pid_t) -1;

korbb's avatar
korbb committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
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;
#ifdef DO_STATS
int process_ct = 0;
int apply_ct = 0;
int fixed_ct = 0;
int altered_ct = 0;
#endif /* DO_STATS */

#ifdef HAVE_MMAP
#define UNLOAD_DATA() do { if (curr_data_mapped) { \
  munmap ((void*)pz_curr_data, data_map_size); close (data_map_fd); } \
  else free ((void*)pz_curr_data); } while(0)
#else
#define UNLOAD_DATA() free ((void*)pz_curr_data)
#endif

korbb's avatar
korbb committed
133
const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
korbb's avatar
korbb committed
134
tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
korbb's avatar
korbb committed
135 136
regex_t incl_quote_re;

korbb's avatar
korbb committed
137
void do_version ();
138 139
char *load_file  _P_((const char *));
void process  _P_((char *, const char *));
korbb's avatar
korbb committed
140 141
void run_compiles ();
void initialize ();
korbb's avatar
korbb committed
142 143 144
void process ();

/*  External Source Code */
145 146

#include "fixincl.x"
korbb's avatar
korbb committed
147 148
#include "fixtests.c"
#include "fixfixes.c"
149

korbb's avatar
korbb committed
150 151 152 153
/* * * * * * * * * * * * * * * * * * *
 *
 *  MAIN ROUTINE
 */
154 155 156 157 158
int
main (argc, argv)
     int argc;
     char **argv;
{
korbb's avatar
korbb committed
159
  char *file_name_buf;
160

korbb's avatar
korbb committed
161 162 163 164 165 166
  switch (argc)
    {
    case 1:
      break;

    case 2:
167
      if (strcmp (argv[1], "-v") == 0)
korbb's avatar
korbb committed
168 169
        do_version ();
      if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
170
        {
korbb's avatar
korbb committed
171 172 173
          fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
                   errno, strerror (errno), argv[1] );
          exit (EXIT_FAILURE);
174
        }
korbb's avatar
korbb committed
175 176 177 178 179
      break;

    default:
      fputs ("fixincl ERROR:  too many command line arguments\n", stderr);
      exit (EXIT_FAILURE);
180 181
    }

korbb's avatar
korbb committed
182 183
  initialize ();

korbb's avatar
korbb committed
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
  /* 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);
    }

korbb's avatar
korbb committed
199 200
  for (;;)
    {
korbb's avatar
korbb committed
201
      char* pz_end;
korbb's avatar
korbb committed
202

korbb's avatar
korbb committed
203
      /*  skip to start of name, past any "./" prefixes */
korbb's avatar
korbb committed
204

korbb's avatar
korbb committed
205 206 207
      while (ISSPACE (*file_name_buf))  file_name_buf++;
      while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
        file_name_buf += 2;
korbb's avatar
korbb committed
208

korbb's avatar
korbb committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
      /*  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;
korbb's avatar
korbb committed
230

korbb's avatar
korbb committed
231 232 233 234
#ifdef NO_BOGOSITY
      process ();
#else
      /*  Prevent duplicate output by child process  */
korbb's avatar
korbb committed
235

236 237 238
      fflush (stdout);
      fflush (stderr);

korbb's avatar
korbb committed
239
      {
korbb's avatar
korbb committed
240
        void wait_for_pid _P_(( pid_t ));
korbb's avatar
korbb committed
241 242
        pid_t child = fork ();
        if (child == NULLPROCESS)
korbb's avatar
korbb committed
243 244 245 246
          {
            process ();
            return EXIT_SUCCESS;
          }
korbb's avatar
korbb committed
247 248 249 250 251 252 253 254

        if (child == NOPROCESS)
          {
            fprintf (stderr, "Error %d (%s) forking in main\n",
                     errno, strerror (errno));
            exit (EXIT_FAILURE);
          }

korbb's avatar
korbb committed
255
        wait_for_pid( child );
korbb's avatar
korbb committed
256 257
      }
#endif
korbb's avatar
korbb committed
258
    } /*  for (;;) */
korbb's avatar
korbb committed
259

korbb's avatar
korbb committed
260 261 262 263 264 265 266 267 268 269 270 271
#ifdef DO_STATS
  {
    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 */
korbb's avatar
korbb committed
272 273 274 275
  return EXIT_SUCCESS;
}


korbb's avatar
korbb committed
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
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.  we can correctly run our server shell process
     3.  that we can compile all the regular expressions.
  */
  run_compiles ();
  sprintf (zBuf, zFmt, program_id);
  fputs (zBuf + 5, stdout);
  exit (strcmp (run_shell (zBuf), program_id));
}

korbb's avatar
korbb committed
293 294 295
/* * * * * * * * * * * * */

void
korbb's avatar
korbb committed
296
initialize ()
korbb's avatar
korbb committed
297 298
{
  static const char var_not_found[] =
korbb's avatar
korbb committed
299 300
    "fixincl ERROR:  %s environment variable not defined\n\
\tTARGET_MACHINE, DESTDIR, SRCDIR and FIND_BASE are required\n";
korbb's avatar
korbb committed
301

302
  {
korbb's avatar
korbb committed
303 304 305
    static const char var[] = "TARGET_MACHINE";
    pz_machine = getenv (var);
    if (pz_machine == (char *) NULL)
306
      {
korbb's avatar
korbb committed
307
        fprintf (stderr, var_not_found, var);
308 309 310 311 312
        exit (EXIT_FAILURE);
      }
  }

  {
korbb's avatar
korbb committed
313 314 315
    static const char var[] = "DESTDIR";
    pz_dest_dir = getenv (var);
    if (pz_dest_dir == (char *) NULL)
316
      {
korbb's avatar
korbb committed
317
        fprintf (stderr, var_not_found, var);
318 319 320 321 322
        exit (EXIT_FAILURE);
      }
  }

  {
korbb's avatar
korbb committed
323 324 325
    static const char var[] = "SRCDIR";
    pz_src_dir = getenv (var);
    if (pz_src_dir == (char *) NULL)
326
      {
korbb's avatar
korbb committed
327
        fprintf (stderr, var_not_found, var);
328 329 330 331
        exit (EXIT_FAILURE);
      }
  }

332 333
  {
    static const char var[] = "FIND_BASE";
korbb's avatar
korbb committed
334 335
    char *pz = getenv (var);
    if (pz == (char *) NULL)
336 337 338 339
      {
        fprintf (stderr, var_not_found, var);
        exit (EXIT_FAILURE);
      }
korbb's avatar
korbb committed
340 341 342 343
    while ((pz[0] == '.') && (pz[1] == '/'))
      pz += 2;
    if ((pz[0] != '.') || (pz[1] != NUL))
      find_base_len = strlen( pz );
344 345
  }

korbb's avatar
korbb committed
346 347 348 349
  /*  Compile all the regular expressions now.
      That way, it is done only once for the whole run.
      */
  run_compiles ();
350

korbb's avatar
korbb committed
351 352 353 354 355
  signal (SIGQUIT, SIG_IGN);
  signal (SIGIOT,  SIG_IGN);
  signal (SIGPIPE, SIG_IGN);
  signal (SIGALRM, SIG_IGN);
  signal (SIGTERM, SIG_IGN);
korbb's avatar
korbb committed
356
#ifndef NO_BOGOSITY
korbb's avatar
korbb committed
357 358 359 360
  /*
     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.  */
361

korbb's avatar
korbb committed
362 363
  close_server ();
  (void)wait ( (int*)NULL );
korbb's avatar
korbb committed
364
#endif
korbb's avatar
korbb committed
365
}
366

korbb's avatar
korbb committed
367
#ifndef NO_BOGOSITY
korbb's avatar
korbb committed
368
/* * * * * * * * * * * * *
korbb's avatar
korbb committed
369

korbb's avatar
korbb committed
370 371 372
   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. */
373

korbb's avatar
korbb committed
374
void
korbb's avatar
korbb committed
375
wait_for_pid(child)
376
     pid_t child;
korbb's avatar
korbb committed
377 378 379 380
{
  for (;;) {
    int status;
    pid_t dead_kid = wait (&status);
381

korbb's avatar
korbb committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    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 */
      }
korbb's avatar
korbb committed
398

korbb's avatar
korbb committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
    /*
       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;
416
        }
korbb's avatar
korbb committed
417 418
      }
  } done_waiting:;
419
}
korbb's avatar
korbb committed
420
#endif /* NO_BOGOSITY */
421

korbb's avatar
korbb committed
422
/* * * * * * * * * * * * *
korbb's avatar
korbb committed
423

korbb's avatar
korbb committed
424 425 426 427
   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.  */
428
char *
korbb's avatar
korbb committed
429 430
load_file ( fname )
    const char* fname;
431
{
korbb's avatar
korbb committed
432 433
  struct stat stbf;
  char* res;
434

korbb's avatar
korbb committed
435
  if (stat (fname, &stbf) != 0)
436
    {
korbb's avatar
korbb committed
437 438 439
      fprintf (stderr, "error %d (%s) stat-ing %s\n",
               errno, strerror (errno), fname );
      return (char *) NULL;
440
    }
korbb's avatar
korbb committed
441 442
  if (stbf.st_size == 0)
    return (char*)NULL;
443

korbb's avatar
korbb committed
444 445 446
  data_map_size = stbf.st_size+1;
  data_map_fd   = open (fname, O_RDONLY);
  ttl_data_size += data_map_size-1;
447

korbb's avatar
korbb committed
448 449 450 451 452 453
  if (data_map_fd < 0)
    {
      fprintf (stderr, "error %d (%s) opening %s for read\n",
               errno, strerror (errno), fname);
      return (char*)NULL;
    }
454

korbb's avatar
korbb committed
455 456 457 458 459 460 461 462 463 464 465 466 467
#ifdef HAVE_MMAP
  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
468

korbb's avatar
korbb committed
469
  return res;
470 471 472
}


korbb's avatar
korbb committed
473
/* * * * * * * * * * * * *
korbb's avatar
korbb committed
474

korbb's avatar
korbb committed
475 476
   run_compiles   run all the regexp compiles for all the fixes once.
 */
477
void
korbb's avatar
korbb committed
478
run_compiles ()
479
{
480 481
  tSCC z_bad_comp[] = "fixincl ERROR:  cannot compile %s regex for %s\n\
\texpr = `%s'\n\terror %s\n";
korbb's avatar
korbb committed
482 483 484 485 486 487 488 489 490
  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));

  if (p_re == (regex_t *) NULL)
491 492 493 494 495 496
    {
      fprintf (stderr, "fixincl ERROR:  cannot allocate %d bytes for regex\n",
               REGEX_COUNT * sizeof (regex_t));
      exit (EXIT_FAILURE);
    }

korbb's avatar
korbb committed
497 498 499 500 501 502
  /*  Make sure re_compile_pattern 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) );

korbb's avatar
korbb committed
503 504 505 506 507
  /*  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.  */

508
  re_set_syntax (RE_SYNTAX_EGREP);
korbb's avatar
korbb committed
509 510 511
  pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
                              &incl_quote_re);
  if (pz_err != (char *) NULL)
512
    {
korbb's avatar
korbb committed
513 514
      fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
               incl_quote_pat, pz_err);
515 516 517
      exit (EXIT_FAILURE);
    }

korbb's avatar
korbb committed
518
  /* FOR every fixup, ...  */
519 520
  do
    {
korbb's avatar
korbb committed
521 522
      p_test = p_fixd->p_test_desc;
      test_ct = p_fixd->test_ct;
523

korbb's avatar
korbb committed
524 525 526 527 528 529 530
      /*  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) )
        {
korbb's avatar
korbb committed
531
          tSCC case_fmt[] = "case %s in\n";     /*  9 bytes, plus string */
532 533
          tSCC esac_fmt[] =
               " )\n    echo %s ;;\n* ) echo %s ;;\nesac";/*  4 bytes */
korbb's avatar
korbb committed
534 535 536 537
          tSCC skip[] = "skip";                 /*  4 bytes */
          tSCC run[] = "run";                   /*  3 bytes */
          /* total bytes to add to machine sum:    49 - see fixincl.tpl */

korbb's avatar
korbb committed
538
          const char **papz_machs = p_fixd->papz_machs;
korbb's avatar
korbb committed
539
          char *pz;
korbb's avatar
korbb committed
540 541 542
          char *pz_sep = "";
          tCC *pz_if_true;
          tCC *pz_if_false;
korbb's avatar
korbb committed
543
          char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
korbb's avatar
korbb committed
544

korbb's avatar
korbb committed
545
          /* Start the case statement */
korbb's avatar
korbb committed
546

korbb's avatar
korbb committed
547 548
          sprintf (cmd_buf, case_fmt, pz_machine);
          pz = cmd_buf + strlen (cmd_buf);
korbb's avatar
korbb committed
549

korbb's avatar
korbb committed
550
          /*  Determine if a match means to apply the fix or not apply it */
korbb's avatar
korbb committed
551 552 553 554 555 556 557 558 559 560 561 562

          if (p_fixd->fd_flags & FD_MACH_IFNOT)
            {
              pz_if_true  = skip;
              pz_if_false = run;
            }
          else
            {
              pz_if_true  = run;
              pz_if_false = skip;
            }

korbb's avatar
korbb committed
563 564
          /*  Emit all the machine names.  If there are more than one,
              then we will insert " | \\\n" between the names  */
korbb's avatar
korbb committed
565 566 567 568 569 570 571

          for (;;)
            {
              const char* pz_mach = *(papz_machs++);

              if (pz_mach == (const char*) NULL)
                break;
korbb's avatar
korbb committed
572
              sprintf (pz, "%s%s", pz_sep, pz_mach);
korbb's avatar
korbb committed
573 574 575
              pz += strlen (pz);
              pz_sep = " | \\\n";
            }
korbb's avatar
korbb committed
576 577 578 579

          /* Now emit the match and not-match actions and the esac */

          sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
korbb's avatar
korbb committed
580 581 582 583

          /*  Run the script.
              The result will start either with 's' or 'r'.  */

korbb's avatar
korbb committed
584 585
          {
            int skip;
korbb's avatar
korbb committed
586
            pz = run_shell (cmd_buf);
korbb's avatar
korbb committed
587 588 589 590 591 592 593 594
            skip = (*pz == 's');
            free ( (void*)pz );
            if (skip)
              {
                p_fixd->fd_flags |= FD_SKIP_TEST;
                continue;
              }
           }
595 596
        }

korbb's avatar
korbb committed
597 598 599
      /* FOR every test for the fixup, ...  */

      while (--test_ct >= 0)
600
        {
korbb's avatar
korbb committed
601
          switch (p_test->type)
602 603 604
            {
            case TT_EGREP:
            case TT_NEGREP:
korbb's avatar
korbb committed
605 606 607 608 609
              /*  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)
610 611 612 613 614
                {
                  fputs ("out of RE's\n", stderr);
                  exit (EXIT_FAILURE);
                }

korbb's avatar
korbb committed
615 616 617 618 619
              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)
620
                {
korbb's avatar
korbb committed
621 622
                  fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name,
                           p_test->pz_test_text, pz_err);
623 624 625
                  exit (EXIT_FAILURE);
                }
            }
korbb's avatar
korbb committed
626
          p_test++;
627 628
        }
    }
korbb's avatar
korbb committed
629
  while (p_fixd++, --fix_ct > 0);
630 631 632
}


korbb's avatar
korbb committed
633
/* * * * * * * * * * * * *
korbb's avatar
korbb committed
634

korbb's avatar
korbb committed
635 636 637 638
   create_file  Create the output modified file.
   Input:    the name of the file to create
   Returns:  a file pointer to the new, open file  */

korbb's avatar
korbb committed
639
#define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
korbb's avatar
korbb committed
640

641
FILE *
korbb's avatar
korbb committed
642
create_file ()
643 644 645 646 647
{
  int fd;
  FILE *pf;
  char fname[MAXPATHLEN];

korbb's avatar
korbb committed
648
  sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
649

korbb's avatar
korbb committed
650
  fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
651

korbb's avatar
korbb committed
652
  /*  We may need to create the directories needed... */
653 654
  if ((fd < 0) && (errno == ENOENT))
    {
korbb's avatar
korbb committed
655
      char *pz_dir = strchr (fname + 1, '/');
656 657
      struct stat stbf;

korbb's avatar
korbb committed
658
      while (pz_dir != (char *) NULL)
659
        {
korbb's avatar
korbb committed
660
          *pz_dir = NUL;
661 662 663 664 665 666
          if (stat (fname, &stbf) < 0)
            {
              mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
                     | S_IROTH | S_IXOTH);
            }

korbb's avatar
korbb committed
667 668
          *pz_dir = '/';
          pz_dir = strchr (pz_dir + 1, '/');
669
        }
korbb's avatar
korbb committed
670 671 672

      /*  Now, lets try the open again... */
      fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
673 674 675 676 677 678 679
    }
  if (fd < 0)
    {
      fprintf (stderr, "Error %d (%s) creating %s\n",
               errno, strerror (errno), fname);
      exit (EXIT_FAILURE);
    }
korbb's avatar
korbb committed
680
  fprintf (stderr, "Fixed:  %s\n", pz_curr_file);
681 682 683 684
  pf = fdopen (fd, "w");

#ifdef LATER
  {
korbb's avatar
korbb committed
685 686 687 688 689 690
    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";

korbb's avatar
korbb committed
691
    fprintf (pf, hdr, pz_curr_file);
692 693 694 695 696 697
  }
#endif
  return pf;
}


korbb's avatar
korbb committed
698
/* * * * * * * * * * * * *
699

korbb's avatar
korbb committed
700 701 702
  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
korbb's avatar
korbb committed
703
  Result: APPLY_FIX or SKIP_FIX, depending on the result of the
korbb's avatar
korbb committed
704 705
          shell script we run.  */

korbb's avatar
korbb committed
706 707
int
test_test (p_test, pz_test_file)
korbb's avatar
korbb committed
708
     tTestDesc *p_test;
korbb's avatar
korbb committed
709
     char*      pz_test_file;
korbb's avatar
korbb committed
710
{
711 712 713 714 715 716 717
  tSCC cmd_fmt[] =
"file=%s\n\
if ( test %s ) > /dev/null 2>&1\n\
then echo TRUE\n\
else echo FALSE\n\
fi";

korbb's avatar
korbb committed
718
  char *pz_res;
korbb's avatar
korbb committed
719
  int res = SKIP_FIX;
korbb's avatar
korbb committed
720 721 722

  static char cmd_buf[4096];

korbb's avatar
korbb committed
723
  sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
korbb's avatar
korbb committed
724 725
  pz_res = run_shell (cmd_buf);
  if (*pz_res == 'T')
korbb's avatar
korbb committed
726
    res = APPLY_FIX;
korbb's avatar
korbb committed
727
  free ((void *) pz_res);
728 729 730 731
  return res;
}


korbb's avatar
korbb committed
732
/* * * * * * * * * * * * *
korbb's avatar
korbb committed
733

korbb's avatar
korbb committed
734 735 736
  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
korbb's avatar
korbb committed
737
  Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
korbb's avatar
korbb committed
738

korbb's avatar
korbb committed
739
  The caller may choose to reverse meaning if the sense of the test
korbb's avatar
korbb committed
740 741
  is inverted.  */

korbb's avatar
korbb committed
742
int
korbb's avatar
korbb committed
743 744 745
egrep_test (pz_data, p_test)
     char *pz_data;
     tTestDesc *p_test;
746 747
{
  regmatch_t match;
korbb's avatar
korbb committed
748

korbb's avatar
korbb committed
749
#ifdef DEBUG
korbb's avatar
korbb committed
750 751 752
  if (p_test->p_test_regex == 0)
    fprintf (stderr, "fixincl ERROR RE not compiled:  `%s'\n",
             p_test->pz_test_text);
753
#endif
korbb's avatar
korbb committed
754
  if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
korbb's avatar
korbb committed
755 756
    return APPLY_FIX;
  return SKIP_FIX;
757 758 759
}


760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
/* * * * * * * * * * * * *

  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.  */

int
quoted_file_exists (pz_src_path, pz_file_path, pz_file)
     char* pz_src_path;
     char* pz_file_path;
     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++;
korbb's avatar
korbb committed
779
    if (! ISGRAPH( ch ))
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
      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 );
  }
}


korbb's avatar
korbb committed
795 796 797
/* * * * * * * * * * * * *
 *
   extract_quoted_files
korbb's avatar
korbb committed
798

korbb's avatar
korbb committed
799 800 801 802 803 804 805
   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:
korbb's avatar
korbb committed
806

korbb's avatar
korbb committed
807 808 809 810 811 812 813 814
    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  */
815

816

817
void
korbb's avatar
korbb committed
818
extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
korbb's avatar
korbb committed
819
     char *pz_data;
korbb's avatar
korbb committed
820
     const char *pz_fixed_file;
korbb's avatar
korbb committed
821
     regmatch_t *p_re_match;
822
{
korbb's avatar
korbb committed
823
  char *pz_dir_end = strrchr (pz_fixed_file, '/');
korbb's avatar
korbb committed
824
  char *pz_incl_quot = pz_data;
825

korbb's avatar
korbb committed
826
  fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
827

korbb's avatar
korbb committed
828
  /*  Set "pz_fixed_file" to point to the containing subdirectory of the source
829
      If there is none, then it is in our current directory, ".".   */
korbb's avatar
korbb committed
830 831

  if (pz_dir_end == (char *) NULL)
korbb's avatar
korbb committed
832
    pz_fixed_file = ".";
833
  else
korbb's avatar
korbb committed
834
    *pz_dir_end = '\0';
835 836 837

  for (;;)
    {
korbb's avatar
korbb committed
838 839 840
      pz_incl_quot += p_re_match->rm_so;

      /*  Skip forward to the included file name */
841
      while (ISSPACE (*pz_incl_quot))
korbb's avatar
korbb committed
842
        pz_incl_quot++;
843
      /* ISSPACE() may evaluate is argument more than once!  */
korbb's avatar
korbb committed
844
      while (++pz_incl_quot, ISSPACE (*pz_incl_quot))
korbb's avatar
korbb committed
845 846 847 848 849
        ;
      pz_incl_quot += sizeof ("include") - 1;
      while (*pz_incl_quot++ != '"')
        ;

korbb's avatar
korbb committed
850
      if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
851 852 853
        {
          /* Print the source directory and the subdirectory
             of the file in question.  */
korbb's avatar
korbb committed
854
          printf ("%s  %s/", pz_src_dir, pz_fixed_file);
855 856 857 858 859 860 861 862
          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 */
korbb's avatar
korbb committed
863
          printf ("  %s/%s/", pz_dest_dir, pz_fixed_file);
864 865 866 867 868 869
          while (*pz_dir_end != '"')
            putc (*pz_dir_end++, stdout);

          /* End of entry */
          putc ('\n', stdout);
        }
870

korbb's avatar
korbb committed
871 872
      /* Find the next entry */
      if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
873 874 875 876 877
        break;
    }
}


korbb's avatar
korbb committed
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
/* * * * * * * * * * * * *

    Somebody wrote a *_fix subroutine that we must call.
    */

int
internal_fix (read_fd, p_fixd)
  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, strerror (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.
   */
  fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
  fcntl (read_fd, F_DUPFD, STDIN_FILENO);
  fdopen (STDIN_FILENO, "r");
  fdopen (STDOUT_FILENO, "w");

  apply_fix (p_fixd->patch_args[0], pz_curr_file);
  exit (0);
}

korbb's avatar
korbb committed
951 952 953 954 955 956 957 958 959

/* * * * * * * * * * * * *

    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
korbb's avatar
korbb committed
960
start_fixer (read_fd, p_fixd, pz_fix_file)
korbb's avatar
korbb committed
961 962
  int read_fd;
  tFixDesc* p_fixd;
korbb's avatar
korbb committed
963
  char* pz_fix_file;
korbb's avatar
korbb committed
964 965 966 967
{
  tCC* pz_cmd_save;
  char* pz_cmd;

korbb's avatar
korbb committed
968 969 970
  if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
    return internal_fix (read_fd, p_fixd);

korbb's avatar
korbb committed
971 972 973 974 975
  if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
    pz_cmd = (char*)NULL;
  else
    {
      tSCC z_cmd_fmt[] = "file='%s'\n%s";
korbb's avatar
korbb committed
976
      pz_cmd = (char*)malloc (strlen (p_fixd->patch_args[2])
korbb's avatar
korbb committed
977
                               + sizeof( z_cmd_fmt )
korbb's avatar
korbb committed
978 979 980 981 982 983 984
                               + 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]);
korbb's avatar
korbb committed
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
      pz_cmd_save = p_fixd->patch_args[2];
      p_fixd->patch_args[2] = pz_cmd;
    }

  for (;;)
    {
      static int failCt = 0;
      int fd;

      fd = chain_open (read_fd,
                       (t_pchar *) p_fixd->patch_args,
                       (process_chain_head == -1)
                       ? &process_chain_head : (pid_t *) NULL);

      if (fd != -1)
        {
          read_fd = fd;
          break;
        }

korbb's avatar
korbb committed
1005
      fprintf (stderr, z_fork_err, errno, strerror (errno),
korbb's avatar
korbb committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
               p_fixd->fix_name);

      if ((errno != EAGAIN) || (++failCt > 10))
        exit (EXIT_FAILURE);
      sleep (1);
    }

  if (pz_cmd != (char*)NULL)
    {
      free ((void*)pz_cmd);
      p_fixd->patch_args[2] = pz_cmd_save;
    }

  return read_fd;
}

korbb's avatar
korbb committed
1022

korbb's avatar
korbb committed
1023 1024 1025 1026 1027 1028
/* * * * * * * * * * * * *

   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.  */

korbb's avatar
korbb committed
1029 1030 1031
t_bool
fix_applies (p_fixd)
  tFixDesc *p_fixd;
1032
{
korbb's avatar
korbb committed
1033 1034
  int test_ct;
  tTestDesc *p_test;
1035

korbb's avatar
korbb committed
1036 1037
  if (p_fixd->fd_flags & FD_SKIP_TEST)
    return BOOL_FALSE;
1038

korbb's avatar
korbb committed
1039 1040 1041 1042 1043 1044 1045 1046
  /*  IF there is a file name restriction,
      THEN ensure the current file name matches one in the pattern  */

  if (p_fixd->file_list != (char *) NULL)
    {
      const char *pz_fname = pz_curr_file;
      const char *pz_scan = p_fixd->file_list;
      size_t name_len;
1047

korbb's avatar
korbb committed
1048 1049 1050
      while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
        pz_fname += 2;
      name_len = strlen (pz_fname);
korbb's avatar
korbb committed
1051

korbb's avatar
korbb committed
1052
      for (;;)
1053
        {
korbb's avatar
korbb committed
1054 1055 1056 1057 1058
          pz_scan = strstr (pz_scan + 1, pz_fname);
          /*  IF we can't match the string at all,
              THEN bail  */
          if (pz_scan == (char *) NULL)
            return BOOL_FALSE;
1059

korbb's avatar
korbb committed
1060 1061
          /*  IF the match is surrounded by the '|' markers,
              THEN we found a full match -- time to run the tests  */
1062

korbb's avatar
korbb committed
1063 1064 1065 1066
          if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
            break;
        }
    }
1067

korbb's avatar
korbb committed
1068 1069
  /*  FOR each test, see if it fails.
      IF it does fail, then we go on to the next test */
korbb's avatar
korbb committed
1070

korbb's avatar
korbb committed
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
  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)
            return BOOL_FALSE;
          break;

        case TT_EGREP:
          if (egrep_test (pz_curr_data, p_test) != APPLY_FIX)
            return BOOL_FALSE;
          break;

        case TT_NEGREP:
          if (egrep_test (pz_curr_data, p_test) == APPLY_FIX)
            /*  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)
            return BOOL_FALSE;
          break;
1098
        }
korbb's avatar
korbb committed
1099
    }
1100

korbb's avatar
korbb committed
1101 1102
  return BOOL_TRUE;
}
1103 1104


korbb's avatar
korbb committed
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
/* * * * * * * * * * * * *

   Write out a replacement file  */

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++;
korbb's avatar
korbb committed
1145
#endif
korbb's avatar
korbb committed
1146 1147 1148
  for (;;)
    {
      int ch;
1149

korbb's avatar
korbb committed
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
      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)
        {
          out_fp = create_file (pz_curr_file);

#ifdef DO_STATS
          altered_ct++;
korbb's avatar
korbb committed
1169
#endif
korbb's avatar
korbb committed
1170 1171 1172 1173 1174 1175
          /*  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);

          /*  Emit the current unmatching character */
          putc (ch, out_fp);
1176
        }
korbb's avatar
korbb committed
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
      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 (regexec (&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 ()
{
  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, strerror (erno));
      return;
    }

  pz_curr_data = load_file (pz_curr_file);
  if (pz_curr_data == (char *) NULL)
    return;

#ifdef DO_STATS
  process_ct++;
#endif
  fprintf (stderr, "%6d %-50s   \r", data_map_size, pz_curr_file );
  if (strstr (pz_curr_data, gnu_lib_mark) != (char *) NULL)
    {
      UNLOAD_DATA();
      return;
    }

  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;
1244

1245
      fprintf (stderr, "Applying %-24s to %s\n",
korbb's avatar
korbb committed
1246 1247 1248 1249 1250 1251 1252 1253
               p_fixd->fix_name, pz_curr_file);

      if (p_fixd->fd_flags & FD_REPLACEMENT)
        {
          write_replacement (p_fixd);
          UNLOAD_DATA();
          return;
        }
korbb's avatar
korbb committed
1254 1255 1256 1257 1258

      /*  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
korbb's avatar
korbb committed
1259
          stdout descriptor of the previous fix for its stdin.  */
1260

1261
      if (read_fd == -1)
korbb's avatar
korbb committed
1262
        {
korbb's avatar
korbb committed
1263
          read_fd = open (pz_curr_file, O_RDONLY);
1264
          if (read_fd < 0)
1265
            {
korbb's avatar
korbb committed
1266
              fprintf (stderr, "Error %d (%s) opening %s\n", errno,
korbb's avatar
korbb committed
1267
                       strerror (errno), pz_curr_file);
korbb's avatar
korbb committed
1268
              exit (EXIT_FAILURE);
1269
            }
korbb's avatar
korbb committed
1270 1271 1272 1273

          /*  Ensure we do not get duplicate output */

          fflush (stdout);
korbb's avatar
korbb committed
1274
        }
1275

korbb's avatar
korbb committed
1276
      read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
korbb's avatar
korbb committed
1277
      num_children++;
1278 1279
    }

korbb's avatar
korbb committed
1280 1281
  /*  IF we have a read-back file descriptor,
      THEN check for changes and write output if changed.   */
korbb's avatar
korbb committed
1282

korbb's avatar
korbb committed
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
  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);
    }
korbb's avatar
korbb committed
1295

korbb's avatar
korbb committed
1296
  UNLOAD_DATA();
1297
}