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.
6 Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
8 This file is part of GNU CC.
10 GNU CC is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
15 GNU CC is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with GNU CC; see the file COPYING. If not, write to
22 the Free Software Foundation, 59 Temple Place - Suite 330,
23 Boston, MA 02111-1307, USA. */
26 /* The fixincl program is known to not run properly on this particular
27 system. Instead of producing a probably broken executable, we force
28 a compilation error and let the mkfixinc.sh script install the
29 inclhack.sh shell script instead. */
30 # include "The fixincl program does not work properly on this system!"
33 #include "auto-host.h"
35 #include <sys/types.h>
36 #include <sys/param.h>
38 #ifdef HAVE_SYS_WAIT_H
54 #include "gnu-regex.h"
57 static const char program_id[] = "fixincl version 1.0";
59 #define MINIMUM_MAXIMUM_LINES 128
61 /* If this particular system's header files define the macro `MAXPATHLEN',
62 we happily take advantage of it; otherwise we use a value which ought
63 to be large enough. */
65 # define MAXPATHLEN 4096
67 #define NAME_TABLE_SIZE (MINIMUM_MAXIMUM_LINES * MAXPATHLEN)
71 #define tSCC static const char
72 #define tCC const char
73 #define tSC static char
75 typedef int t_success;
81 #define SUCCEEDED(p) ((p) == SUCCESS)
82 #define SUCCESSFUL(p) SUCCEEDED (p)
83 #define FAILED(p) ((p) < SUCCESS)
84 #define HADGLITCH(p) ((p) > SUCCESS)
90 Each fix may have associated tests that determine
91 whether the fix needs to be applied or not.
92 Each test has a type (from the te_test_type enumeration);
93 associated test text; and, if the test is TT_EGREP or
94 the negated form TT_NEGREP, a pointer to the compiled
95 version of the text string.
100 TT_TEST, TT_EGREP, TT_NEGREP
103 typedef struct test_desc tTestDesc;
108 const char *pz_test_text;
109 regex_t *p_test_regex;
112 typedef struct patch_desc tPatchDesc;
116 Everything you ever wanted to know about how to apply
117 a particular fix (which files, how to qualify them,
118 how to actually make the fix, etc...)
121 #define FD_MACH_ONLY 0x0000
122 #define FD_MACH_IFNOT 0x0001
123 #define FD_SKIP_TEST 0x8000
125 typedef struct fix_desc tFixDesc;
128 const char* fix_name; /* Name of the fix */
129 const char* file_list; /* List of files it applies to */
130 const char** papz_machs; /* List of machine/os-es it applies to */
134 tTestDesc* p_test_desc;
135 const char** patch_args;
138 /* Working environment strings. Essentially, invocation 'options'. */
139 char *pz_dest_dir = NULL;
140 char *pz_src_dir = NULL;
141 char *pz_machine = NULL;
142 char *pz_find_base = NULL;
143 int find_base_len = 0;
145 pid_t process_chain_head = (pid_t) -1;
147 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
148 regex_t incl_quote_re;
150 char *load_file (const char *);
151 void process (char *, const char *);
152 void run_compiles ();
153 void wait_for_pid( pid_t, int );
158 /* * * * * * * * * * * * * * * * * * *
167 static const char gnu_lib_mark[] =
168 "This file is part of the GNU C Library";
170 #ifndef NO_BOGOSITY_LIMITS
171 # define BOGUS_LIMIT MINIMUM_MAXIMUM_LINES
175 char *apz_names[BOGUS_LIMIT];
178 /* Before anything else, ensure we can allocate our file name buffer. */
179 file_name_buf = (char *) malloc (NAME_TABLE_SIZE);
180 if (file_name_buf == (char *) NULL)
182 fprintf (stderr, "fixincl cannot allocate 0x%08X bytes\n",
193 if (strcmp (argv[1], "-v") == 0)
195 static const char zFmt[] = "echo '%s'";
197 /* The 'version' option is really used to test that:
198 1. The program loads correctly (no missing libraries)
199 2. we can correctly run our server shell process
200 3. that we can compile all the regular expressions.
203 sprintf (file_name_buf, zFmt, program_id);
204 fputs (file_name_buf + 5, stdout);
205 exit (strcmp (run_shell (file_name_buf), program_id));
207 freopen (argv[1], "r", stdin);
211 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
217 #ifndef NO_BOGOSITY_LIMITS
218 /* Some systems only allow so many calls to fork(2).
219 This is inadequate for this program. Consequently,
220 we must let a grandfather process spawn children
221 that then spawn all the processes that do the real work.
228 char *pz_buf = file_name_buf;
230 /* Only the parent process can read from stdin without confusing
231 the world. (How does the child tell the parent to skip
232 forward? Pipes and files behave differently.) */
234 while ( (file_name_ct < BOGUS_LIMIT)
235 && (pz_buf < (file_name_buf + NAME_TABLE_SIZE - MAXPATHLEN)))
237 if (fgets (pz_buf, MAXPATHLEN, stdin) == (char *) NULL)
239 while (isspace (*pz_buf))
241 if ((*pz_buf == '\0') || (*pz_buf == '#'))
243 apz_names[file_name_ct++] = pz_buf;
244 pz_buf += strlen (pz_buf);
245 while (isspace (pz_buf[-1]))
251 /* IF we did not get any files this time thru
252 THEN we must be done. */
253 if (file_name_ct == 0)
257 pid_t child = fork ();
258 if (child == NULLPROCESS)
261 if (child == NOPROCESS)
263 fprintf (stderr, "Error %d (%s) forking in main\n",
264 errno, strerror (errno));
268 wait_for_pid( child, file_name_ct );
272 #error "NON-BOGUS LIMITS NOT SUPPORTED?!?!"
276 Here we are the child of the grandparent process. The parent
277 of all the little fixup processes. We ignore the deaths of
280 signal (SIGCLD, SIG_IGN);
283 fprintf (stderr, "Child start -- processing %d files\n",
287 /* For every file specified in stdandard in
288 (except as throttled for bogus reasons)...
290 for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++)
293 char *pz_file_name = apz_names[loop_ct];
295 if (access (pz_file_name, R_OK) != 0)
298 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
299 pz_file_name, getcwd ((char *) NULL, MAXPATHLEN),
300 erno, strerror (erno));
302 else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL))
304 if (strstr (pz_data, gnu_lib_mark) == (char *) NULL)
305 process (pz_data, pz_file_name);
306 free ((void *) pz_data);
314 /* * * * * * * * * * * * */
319 static const char var_not_found[] =
320 "fixincl ERROR: %s environment variable not defined\n";
323 static const char var[] = "TARGET_MACHINE";
324 pz_machine = getenv (var);
325 if (pz_machine == (char *) NULL)
327 fprintf (stderr, var_not_found, var);
333 static const char var[] = "DESTDIR";
334 pz_dest_dir = getenv (var);
335 if (pz_dest_dir == (char *) NULL)
337 fprintf (stderr, var_not_found, var);
343 static const char var[] = "SRCDIR";
344 pz_src_dir = getenv (var);
345 if (pz_src_dir == (char *) NULL)
347 fprintf (stderr, var_not_found, var);
353 static const char var[] = "FIND_BASE";
354 pz_find_base = getenv (var);
355 if (pz_find_base == (char *) NULL)
357 fprintf (stderr, var_not_found, var);
360 find_base_len = strlen( pz_find_base );
363 /* Compile all the regular expressions now.
364 That way, it is done only once for the whole run.
368 signal (SIGQUIT, SIG_IGN);
369 signal (SIGIOT, SIG_IGN);
370 signal (SIGPIPE, SIG_IGN);
371 signal (SIGALRM, SIG_IGN);
372 signal (SIGTERM, SIG_IGN);
375 Make sure that if we opened a server process, we close it now.
376 This is the grandparent process. We don't need the server anymore
377 and our children should make their own. */
380 (void)wait ( (int*)NULL );
383 /* * * * * * * * * * * * *
385 wait_for_pid - Keep calling `wait(2)' until it returns
386 the process id we are looking for. Not every system has
387 `waitpid(2)'. We also ensure that the children exit with success. */
390 wait_for_pid( pid_t child, int file_name_ct )
393 fprintf (stderr, "Waiting for %d to complete %d files\n",
394 child, file_name_ct);
399 pid_t dead_kid = wait (&status);
401 if (dead_kid == child)
403 if (! WIFEXITED( status ))
405 fprintf (stderr, "child process %d is hung on signal %d\n",
406 child, WSTOPSIG( status ));
409 if (WEXITSTATUS( status ) != 0)
411 fprintf (stderr, "child process %d exited with status %d\n",
412 child, WEXITSTATUS( status ));
416 fprintf (stderr, "child finished %d files %s\n", file_name_ct,
417 status ? strerror (status & 0xFF) : "ok");
419 break; /* normal child completion */
423 IF there is an error, THEN see if it is retryable.
424 If it is not retryable, then break out of this loop. */
425 if (dead_kid == NOPROCESS)
433 fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
434 errno, strerror( errno ), child );
437 case ECHILD: /* no children to wait for?? */
445 /* * * * * * * * * * * * *
447 load_file loads all the contents of a file into malloc-ed memory.
448 Its argument is the name of the file to read in; the returned
449 result is the NUL terminated contents of the file. The file
450 is presumed to be an ASCII text file containing no NULs. */
452 load_file (pz_file_name)
453 const char *pz_file_name;
461 if (stat (pz_file_name, &stbf) != 0)
463 fprintf (stderr, "error %d (%s) stat-ing %s\n",
464 errno, strerror (errno), pz_file_name);
465 return (char *) NULL;
467 file_size = stbf.st_size;
470 return (char *) NULL;
472 pz_data = (char *) malloc ((file_size + 16) & ~0x00F);
473 if (pz_data == (char *) NULL)
475 fprintf (stderr, "error: could not malloc %d bytes\n",
481 FILE *fp = fopen (pz_file_name, "r");
482 size_t size_left = file_size;
483 char *read_ptr = pz_data;
485 if (fp == (FILE *) NULL)
487 fprintf (stderr, "error %d (%s) opening %s\n", errno,
488 strerror (errno), pz_file_name);
489 free ((void *) pz_data);
490 return (char *) NULL;
495 size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp);
506 fprintf (stderr, "error %d (%s) reading %s\n", err,
507 strerror (err), pz_file_name);
508 free ((void *) pz_data);
510 return (char *) NULL;
514 read_ptr += sizeRead;
515 size_left -= sizeRead;
517 while (size_left != 0);
526 /* * * * * * * * * * * * *
528 run_compiles run all the regexp compiles for all the fixes once.
533 tSCC z_bad_comp[] = "fixincl ERROR: cannot compile %s regex for %s\n"
534 "\texpr = `%s'\n" "\terror %s\n";
535 tFixDesc *p_fixd = fixDescList;
536 int fix_ct = FIX_COUNT;
539 int re_ct = REGEX_COUNT;
541 regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
543 if (p_re == (regex_t *) NULL)
545 fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n",
546 REGEX_COUNT * sizeof (regex_t));
550 /* Make sure re_compile_pattern does not stumble across invalid
553 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
554 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
556 /* The patterns we search for are all egrep patterns.
557 In the shell version of this program, we invoke egrep
558 with the supplied pattern. Here, we will run
559 re_compile_pattern, but it must be using the same rules. */
561 re_set_syntax (RE_SYNTAX_EGREP);
562 pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
564 if (pz_err != (char *) NULL)
566 fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
567 incl_quote_pat, pz_err);
571 /* FOR every fixup, ... */
574 p_test = p_fixd->p_test_desc;
575 test_ct = p_fixd->test_ct;
577 /* IF the machine type pointer is not NULL (we are not in test mode)
578 AND this test is for or not done on particular machines
581 if ( (pz_machine != NULL)
582 && (p_fixd->papz_machs != (const char**) NULL) )
584 const char **papz_machs = p_fixd->papz_machs;
585 char *pz = file_name_buf;
589 tSCC skip[] = "skip";
592 /* Construct a shell script that looks like this:
594 case our-cpu-platform-os in
595 tests-cpu-platform-os-pattern )
601 where 'run' and 'skip' may be reversed, depending on
602 the sense of the test. */
604 sprintf (pz, "case %s in\n", pz_machine);
607 if (p_fixd->fd_flags & FD_MACH_IFNOT)
618 /* FOR any additional machine names to test for,
619 insert the " | \\\n" glue and the machine pattern. */
623 const char* pz_mach = *(papz_machs++);
625 if (pz_mach == (const char*) NULL)
627 sprintf (pz, "%s %s", pz_sep, pz_mach);
631 sprintf (pz, " )\n echo %s ;;\n * )\n echo %s ;;\nesac",
632 pz_if_true, pz_if_false);
635 The result will start either with 's' or 'r'. */
637 pz = run_shell (file_name_buf);
640 p_fixd->fd_flags |= FD_SKIP_TEST;
645 /* FOR every test for the fixup, ... */
647 while (--test_ct >= 0)
649 switch (p_test->type)
653 /* You might consider putting the following under #ifdef.
654 The number of re's used is computed by autogen.
655 So, it is static and known at compile time. */
659 fputs ("out of RE's\n", stderr);
663 p_test->p_test_regex = p_re++;
664 pz_err = re_compile_pattern (p_test->pz_test_text,
665 strlen (p_test->pz_test_text),
666 p_test->p_test_regex);
667 if (pz_err != (char *) NULL)
669 fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name,
670 p_test->pz_test_text, pz_err);
677 while (p_fixd++, --fix_ct > 0);
681 /* * * * * * * * * * * * *
683 create_file Create the output modified file.
684 Input: the name of the file to create
685 Returns: a file pointer to the new, open file */
687 #define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
690 create_file (pz_file_name)
691 const char *pz_file_name;
695 char fname[MAXPATHLEN];
698 if (strncmp( pz_file_name, pz_find_base, find_base_len ) != 0)
700 fprintf (stderr, "Error: input file %s does not match %s/*\n",
701 pz_file_name, pz_find_base );
706 sprintf (fname, "%s/%s", pz_dest_dir, pz_file_name + find_base_len);
708 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
710 /* We may need to create the directories needed... */
711 if ((fd < 0) && (errno == ENOENT))
713 char *pz_dir = strchr (fname + 1, '/');
716 while (pz_dir != (char *) NULL)
719 if (stat (fname, &stbf) < 0)
721 mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
722 | S_IROTH | S_IXOTH);
726 pz_dir = strchr (pz_dir + 1, '/');
729 /* Now, lets try the open again... */
730 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
734 fprintf (stderr, "Error %d (%s) creating %s\n",
735 errno, strerror (errno), fname);
738 fprintf (stderr, "Fixed: %s\n", pz_file_name);
739 pf = fdopen (fd, "w");
743 static const char hdr[] =
744 "/* DO NOT EDIT THIS FILE.\n\n"
745 " It has been auto-edited by fixincludes from /usr/include/%s\n"
746 " This had to be done to correct non-standard usages in the\n"
747 " original, manufacturer supplied header file. */\n\n";
749 fprintf (pf, hdr, pz_file_name);
756 /* * * * * * * * * * * * *
758 test_test make sure a shell-style test expression passes.
759 Input: a pointer to the descriptor of the test to run and
760 the name of the file that we might want to fix
761 Result: SUCCESS or FAILURE, depending on the result of the
762 shell script we run. */
765 test_test (p_test, pz_file_name)
770 t_success res = FAILURE;
772 static char cmd_buf[4096];
775 "if ( test %s ) > /dev/null 2>&1\n"
780 sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text);
781 pz_res = run_shell (cmd_buf);
784 free ((void *) pz_res);
789 /* * * * * * * * * * * * *
791 egrep_test make sure an egrep expression is found in the file text.
792 Input: a pointer to the descriptor of the test to run and
793 the pointer to the contents of the file under suspicion
794 Result: SUCCESS if the pattern is found, FAILURE otherwise
796 The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test
800 egrep_test (pz_data, p_test)
807 if (p_test->p_test_regex == 0)
808 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
809 p_test->pz_test_text);
811 if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
817 /* * * * * * * * * * * * *
821 The syntax, `#include "file.h"' specifies that the compiler is to
822 search the local directory of the current file before the include
823 list. Consequently, if we have modified a header and stored it in
824 another directory, any files that are included by that modified
825 file in that fashion must also be copied into this new directory.
826 This routine finds those flavors of #include and for each one found
829 1. source directory of the original file
830 2. the relative path file name of the #includ-ed file
831 3. the full destination path for this file
833 Input: the text of the file, the file name and a pointer to the
834 match list where the match information was stored.
835 Result: internally nothing. The results are written to stdout
836 for interpretation by the invoking shell */
839 extract_quoted_files (pz_data, pz_file_name, p_re_match)
841 const char *pz_file_name;
842 regmatch_t *p_re_match;
844 char *pz_dir_end = strrchr (pz_file_name, '/');
845 char *pz_incl_quot = pz_data;
847 fprintf (stderr, "Quoted includes in %s\n", pz_file_name);
849 /* Set "pz_file_name" to point to the containing subdirectory of the source
850 If there is none, then it is in our current direcory, ".". */
852 if (pz_dir_end == (char *) NULL)
859 pz_incl_quot += p_re_match->rm_so;
861 /* Skip forward to the included file name */
862 while (isspace (*pz_incl_quot))
864 while (isspace (*++pz_incl_quot))
866 pz_incl_quot += sizeof ("include") - 1;
867 while (*pz_incl_quot++ != '"')
870 /* Print the source directory and the subdirectory of the file
872 printf ("%s %s/", pz_src_dir, pz_file_name);
873 pz_dir_end = pz_incl_quot;
875 /* Append to the directory the relative path of the desired file */
876 while (*pz_incl_quot != '"')
877 putc (*pz_incl_quot++, stdout);
879 /* Now print the destination directory appended with the
880 relative path of the desired file */
881 printf (" %s/%s/", pz_dest_dir, pz_file_name);
882 while (*pz_dir_end != '"')
883 putc (*pz_dir_end++, stdout);
888 /* Find the next entry */
889 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
895 /* * * * * * * * * * * * *
897 Process the potential fixes for a particular include file.
898 Input: the original text of the file and the file's name
899 Result: none. A new file may or may not be created. */
902 process (pz_data, pz_file_name)
904 const char *pz_file_name;
906 static char env_current_file[1024] = { "file=" };
907 tFixDesc *p_fixd = fixDescList;
908 int todo_ct = FIX_COUNT;
909 t_fd_pair fdp = { -1, -1 };
911 /* IF this is the first time through,
912 THEN put the 'file' environment variable into the environment.
913 This is used by some of the subject shell scripts and tests. */
915 if (env_current_file[5] == NUL)
916 putenv (env_current_file);
919 Ghastly as it is, this actually updates the value of the variable:
921 putenv(3C) C Library Functions putenv(3C)
924 putenv() makes the value of the environment variable name
925 equal to value by altering an existing variable or creating
926 a new one. In either case, the string pointed to by string
927 becomes part of the environment, so altering the string will
928 change the environment. string points to a string of the
929 form ``name=value.'' The space used by string is no longer
930 used once a new string-defining name is passed to putenv().
932 strcpy (env_current_file + 5, pz_file_name);
933 process_chain_head = NOPROCESS;
935 /* For every fix in our fix list, ... */
936 for (; todo_ct > 0; p_fixd++, todo_ct--)
941 if (p_fixd->fd_flags & FD_SKIP_TEST)
944 /* IF there is a file name restriction,
945 THEN ensure the current file name matches one in the pattern */
947 if (p_fixd->file_list != (char *) NULL)
949 const char *pz_fname = pz_file_name;
950 const char *pz_scan = p_fixd->file_list;
953 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
955 name_len = strlen (pz_fname);
959 pz_scan = strstr (pz_scan + 1, pz_fname);
960 /* IF we can't match the string at all,
962 if (pz_scan == (char *) NULL)
965 /* IF the match is surrounded by the '|' markers,
966 THEN we found a full match -- time to run the tests */
968 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
973 /* FOR each test, see if it fails.
974 IF it does fail, then we go on to the next test */
976 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
981 static const char z_test_fail[] =
982 "%16s test %2d failed for %s\n";
984 switch (p_test->type)
987 if (!SUCCESSFUL (test_test (p_test, pz_file_name)))
990 fprintf (stderr, z_test_fail, p_fixd->fix_name,
991 p_fixd->test_ct - test_ct, pz_file_name);
998 if (!SUCCESSFUL (egrep_test (pz_data, p_test)))
1001 fprintf (stderr, z_test_fail, p_fixd->fix_name,
1002 p_fixd->test_ct - test_ct, pz_file_name);
1009 if (SUCCESSFUL (egrep_test (pz_data, p_test)))
1012 fprintf (stderr, z_test_fail, p_fixd->fix_name,
1013 p_fixd->test_ct - test_ct, pz_file_name);
1021 fprintf (stderr, "Applying %-24s to %s\n",
1022 p_fixd->fix_name, pz_file_name);
1024 /* IF we do not have a read pointer,
1025 THEN this is the first fix for the current file.
1026 Open the source file. That will be used as stdin for
1027 the first fix. Any subsequent fixes will use the
1028 stdout descriptor of the previous fix as its stdin. */
1030 if (fdp.read_fd == -1)
1032 fdp.read_fd = open (pz_file_name, O_RDONLY);
1033 if (fdp.read_fd < 0)
1035 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1036 strerror (errno), pz_file_name);
1037 exit (EXIT_FAILURE);
1041 /* This loop should only cycle for 1/2 of one loop.
1042 "chain_open" starts a process that uses "fdp.read_fd" as
1043 its stdin and returns the new fd this process will use
1048 static int failCt = 0;
1049 int fd = chain_open (fdp.read_fd,
1050 (t_pchar *) p_fixd->patch_args,
1051 (process_chain_head == -1)
1052 ? &process_chain_head : (pid_t *) NULL);
1060 fprintf (stderr, "Error %d (%s) starting filter process "
1061 "for %s\n", errno, strerror (errno),
1064 if ((errno != EAGAIN) || (++failCt > 10))
1065 exit (EXIT_FAILURE);
1073 /* IF after all the tests we did not start any patch programs,
1076 if (fdp.read_fd < 0)
1079 /* OK. We have work to do. Read back in the output
1080 of the filtering chain. Compare each byte as we read it with
1081 the contents of the original file. As soon as we find any
1082 difference, we will create the output file, write out all
1083 the matched text and then copy any remaining data from the
1084 output of the filter chain.
1087 FILE *in_fp = fdopen (fdp.read_fd, "r");
1088 FILE *out_fp = (FILE *) NULL;
1089 char *pz_cmp = pz_data;
1099 /* IF we are emitting the output
1100 THEN emit this character, too.
1102 if (out_fp != (FILE *) NULL)
1105 /* ELSE if this character does not match the original,
1106 THEN now is the time to start the output.
1108 else if (ch != *pz_cmp)
1110 out_fp = create_file (pz_file_name);
1112 /* IF there are matched data, write it all now. */
1113 if (pz_cmp != pz_data)
1118 fputs (pz_data, out_fp);
1122 /* Emit the current unmatching character */
1126 /* ELSE the character matches. Advance the compare ptr */
1130 /* IF we created the output file, ... */
1131 if (out_fp != (FILE *) NULL)
1135 /* Close the file and see if we have to worry about
1136 `#include "file.h"' constructs. */
1138 if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0)
1139 extract_quoted_files (pz_data, pz_file_name, &match);
1143 close (fdp.read_fd); /* probably redundant, but I'm paranoid */