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