1 /* Install modified versions of certain ANSI-incompatible system header
2 files which are fixed to work correctly with ANSI C and placed in a
3 directory that GNU C will search.
5 Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
7 This file is part of GNU CC.
9 GNU CC is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 GNU CC is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with GNU CC; see the file COPYING. If not, write to
21 the Free Software Foundation, 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA. */
26 #if defined( HAVE_MMAP_FILE )
28 #define BAD_ADDR ((void*)-1)
32 #ifndef SEPARATE_FIX_PROC
36 /* The contents of this string are not very important. It is mostly
37 just used as part of the "I am alive and working" test. */
39 static const char program_id[] = "fixincl version 1.1";
41 /* This format will be used at the start of every generated file */
43 static const char z_std_preamble[] =
44 "/* DO NOT EDIT THIS FILE.\n\n\
45 It has been auto-edited by fixincludes from:\n\n\
47 This had to be done to correct non-standard usages in the\n\
48 original, manufacturer supplied header file. */\n\n";
50 /* Working environment strings. Essentially, invocation 'options'. */
52 #define _ENV_(v,m,n,t) tCC* v = NULL;
56 int find_base_len = 0;
67 te_verbose verbose_level = VERB_PROGRESS;
70 #define VLEVEL(l) (verbose_level >= l)
71 #define NOT_SILENT VLEVEL(VERB_FIXES)
73 pid_t process_chain_head = (pid_t) -1;
75 char* pz_curr_file; /* name of the current file under test/fix */
76 char* pz_curr_data; /* original contents of that file */
77 char* pz_temp_file; /* for DOS, a place to stash the temporary
78 fixed data between system(3) calls */
79 t_bool curr_data_mapped;
82 size_t ttl_data_size = 0;
91 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
92 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
93 regex_t incl_quote_re;
95 static void do_version PARAMS((void)) ATTRIBUTE_NORETURN;
96 char *load_file PARAMS((const char *));
97 void run_compiles PARAMS((void));
98 void initialize PARAMS((int argc,char** argv));
99 void process PARAMS((void));
101 /* External Source Code */
105 /* * * * * * * * * * * * * * * * * * *
109 extern int main PARAMS ((int, char **));
117 initialize ( argc, argv );
119 have_tty = isatty (fileno (stderr));
121 /* Before anything else, ensure we can allocate our file name buffer. */
122 file_name_buf = load_file_data (stdin);
124 /* Because of the way server shells work, you have to keep stdin, out
125 and err open so that the proper input file does not get closed
128 freopen ("/dev/null", "r", stdin);
130 if (file_name_buf == (char *) NULL)
132 fputs ("No file names listed for fixing\n", stderr);
140 /* skip to start of name, past any "./" prefixes */
142 while (ISSPACE (*file_name_buf)) file_name_buf++;
143 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
146 /* Check for end of list */
148 if (*file_name_buf == NUL)
151 /* Set global file name pointer and find end of name */
153 pz_curr_file = file_name_buf;
154 pz_end = strchr( pz_curr_file, '\n' );
155 if (pz_end == (char*)NULL)
156 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
158 file_name_buf = pz_end + 1;
160 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
162 /* IF no name is found (blank line) or comment marker, skip line */
164 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
172 if (VLEVEL( VERB_PROGRESS )) {
175 Processed %5d files containing %d bytes \n\
176 Applying %5d fixes to %d files\n\
177 Altering %5d of them\n";
179 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
180 fixed_ct, altered_ct);
182 #endif /* DO_STATS */
184 # ifdef SEPARATE_FIX_PROC
185 unlink( pz_temp_file );
194 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. that we can compile all the regular expressions.
200 3. we can correctly run our server shell process
203 sprintf (zBuf, zFmt, program_id);
204 #ifndef SEPARATE_FIX_PROC
206 exit (strcmp (run_shell (zBuf), program_id));
208 exit (system (zBuf));
212 /* * * * * * * * * * * * */
215 initialize ( argc, argv )
219 static const char var_not_found[] =
220 "fixincl ERROR: %s environment variable not defined\n"
222 "each of these must be defined:\n"
223 #define _ENV_(v,m,n,t) "\t" n " - " t "\n"
229 xmalloc_set_program_name (argv[0]);
237 if (strcmp (argv[1], "-v") == 0)
239 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
241 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
242 errno, xstrerror (errno), argv[1] );
248 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
252 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
253 v = getenv (var); if (m && (v == NULL)) { \
254 fprintf (stderr, var_not_found, var); \
255 exit (EXIT_FAILURE); } }
261 if (ISDIGIT ( *pz_verbose ))
262 verbose_level = (te_verbose)atoi( pz_verbose );
264 switch (*pz_verbose) {
267 verbose_level = VERB_SILENT; break;
271 verbose_level = VERB_FIXES; break;
275 verbose_level = VERB_APPLIES; break;
279 verbose_level = VERB_PROGRESS; break;
283 verbose_level = VERB_TESTS; break;
287 verbose_level = VERB_EVERYTHING; break;
290 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
292 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
293 find_base_len = strlen( pz_find_base );
295 /* Compile all the regular expressions now.
296 That way, it is done only once for the whole run.
300 # ifdef SEPARATE_FIX_PROC
301 /* NULL as the first argument to `tempnam' causes it to DTRT
302 wrt the temporary directory where the file will be created. */
303 pz_temp_file = tempnam( NULL, "fxinc" );
306 signal (SIGQUIT, SIG_IGN);
308 signal (SIGIOT, SIG_IGN);
311 signal (SIGPIPE, SIG_IGN);
313 signal (SIGALRM, SIG_IGN);
314 signal (SIGTERM, SIG_IGN);
317 /* * * * * * * * * * * * *
319 load_file loads all the contents of a file into malloc-ed memory.
320 Its argument is the name of the file to read in; the returned
321 result is the NUL terminated contents of the file. The file
322 is presumed to be an ASCII text file containing no NULs. */
330 if (stat (fname, &stbf) != 0)
333 fprintf (stderr, "error %d (%s) stat-ing %s\n",
334 errno, xstrerror (errno), fname );
335 return (char *) NULL;
337 if (stbf.st_size == 0)
340 /* Make the data map size one larger than the file size for documentation
341 purposes. Truth is that there will be a following NUL character if
342 the file size is not a multiple of the page size. If it is a multiple,
343 then this adjustment sometimes fails anyway. */
344 data_map_size = stbf.st_size+1;
345 data_map_fd = open (fname, O_RDONLY);
346 ttl_data_size += data_map_size-1;
351 fprintf (stderr, "error %d (%s) opening %s for read\n",
352 errno, xstrerror (errno), fname);
356 #ifdef HAVE_MMAP_FILE
357 curr_data_mapped = BOOL_TRUE;
359 /* IF the file size is a multiple of the page size,
360 THEN sometimes you will seg fault trying to access a trailing byte */
361 if ((stbf.st_size & (getpagesize()-1)) == 0)
362 res = (char*)BAD_ADDR;
364 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
365 MAP_PRIVATE, data_map_fd, 0);
366 if (res == (char*)BAD_ADDR)
369 FILE* fp = fdopen (data_map_fd, "r");
370 curr_data_mapped = BOOL_FALSE;
371 res = load_file_data (fp);
378 static int machine_matches PARAMS ((tFixDesc *));
380 machine_matches( p_fixd )
383 # ifndef SEPARATE_FIX_PROC
384 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
386 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
387 tSCC skip[] = "skip"; /* 4 bytes */
388 tSCC run[] = "run"; /* 3 bytes */
389 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
391 const char **papz_machs = p_fixd->papz_machs;
393 const char *pz_sep = "";
396 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
398 /* Start the case statement */
400 sprintf (cmd_buf, case_fmt, pz_machine);
401 pz = cmd_buf + strlen (cmd_buf);
403 /* Determine if a match means to apply the fix or not apply it */
405 if (p_fixd->fd_flags & FD_MACH_IFNOT)
416 /* Emit all the machine names. If there are more than one,
417 then we will insert " | \\\n" between the names */
421 const char* pz_mach = *(papz_machs++);
423 if (pz_mach == (const char*) NULL)
425 sprintf (pz, "%s%s", pz_sep, pz_mach);
430 /* Now emit the match and not-match actions and the esac */
432 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
435 The result will start either with 's' or 'r'. */
439 pz = run_shell (cmd_buf);
444 p_fixd->fd_flags |= FD_SKIP_TEST;
450 # else /* is SEPARATE_FIX_PROC */
451 const char **papz_machs = p_fixd->papz_machs;
452 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
455 const char* pz_mach = *(papz_machs++);
457 if (pz_mach == (const char*) NULL)
459 if (strstr (pz_mach, "dos") != NULL && !invert)
463 p_fixd->fd_flags |= FD_SKIP_TEST;
468 /* * * * * * * * * * * * *
470 run_compiles run all the regexp compiles for all the fixes once.
475 tFixDesc *p_fixd = fixDescList;
476 int fix_ct = FIX_COUNT;
477 regex_t *p_re = (regex_t *) xmalloc (REGEX_COUNT * sizeof (regex_t));
479 /* Make sure compile_re does not stumble across invalid data */
481 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
482 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
484 compile_re (incl_quote_pat, &incl_quote_re, 1,
485 "quoted include", "run_compiles");
487 /* Allow machine name tests to be ignored (testing, mainly) */
489 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
490 pz_machine = (char*)NULL;
492 /* FOR every fixup, ... */
495 tTestDesc *p_test = p_fixd->p_test_desc;
496 int test_ct = p_fixd->test_ct;
498 /* IF the machine type pointer is not NULL (we are not in test mode)
499 AND this test is for or not done on particular machines
502 if ( (pz_machine != NULL)
503 && (p_fixd->papz_machs != (const char**) NULL)
504 && ! machine_matches (p_fixd) )
507 /* FOR every test for the fixup, ... */
509 while (--test_ct >= 0)
511 switch (p_test->type)
515 p_test->p_test_regex = p_re++;
516 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
517 "select test", p_fixd->fix_name);
523 while (p_fixd++, --fix_ct > 0);
527 /* * * * * * * * * * * * *
529 create_file Create the output modified file.
530 Input: the name of the file to create
531 Returns: a file pointer to the new, open file */
533 #if defined(S_IRUSR) && defined(S_IWUSR) && \
534 defined(S_IRGRP) && defined(S_IROTH)
536 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
538 # define S_IRALL 0644
541 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
542 defined(S_IROTH) && defined(S_IXOTH)
544 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
546 # define S_DIRALL 0755
550 static FILE *create_file PARAMS ((void));
556 char fname[MAXPATHLEN];
558 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
560 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
562 /* We may need to create the directories needed... */
563 if ((fd < 0) && (errno == ENOENT))
565 char *pz_dir = strchr (fname + 1, '/');
568 while (pz_dir != (char *) NULL)
571 if (stat (fname, &stbf) < 0)
573 mkdir (fname, S_IFDIR | S_DIRALL);
577 pz_dir = strchr (pz_dir + 1, '/');
580 /* Now, lets try the open again... */
581 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
585 fprintf (stderr, "Error %d (%s) creating %s\n",
586 errno, xstrerror (errno), fname);
590 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
591 pf = fdopen (fd, "w");
594 * IF pz_machine is NULL, then we are in some sort of test mode.
595 * Do not insert the current directory name. Use a constant string.
597 fprintf (pf, z_std_preamble,
607 /* * * * * * * * * * * * *
609 test_test make sure a shell-style test expression passes.
610 Input: a pointer to the descriptor of the test to run and
611 the name of the file that we might want to fix
612 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
613 shell script we run. */
614 #ifndef SEPARATE_FIX_PROC
615 static int test_test PARAMS ((tTestDesc *, char *));
617 test_test (p_test, pz_test_file)
623 if ( test %s ) > /dev/null 2>&1\n\
631 static char cmd_buf[4096];
633 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
634 pz_res = run_shell (cmd_buf);
646 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
650 free ((void *) pz_res);
655 * IF we are in MS-DOS land, then whatever shell-type test is required
656 * will, by definition, fail
658 #define test_test(t,tf) SKIP_FIX
661 /* * * * * * * * * * * * *
663 egrep_test make sure an egrep expression is found in the file text.
664 Input: a pointer to the descriptor of the test to run and
665 the pointer to the contents of the file under suspicion
666 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
668 The caller may choose to reverse meaning if the sense of the test
671 static int egrep_test PARAMS ((char *, tTestDesc *));
673 egrep_test (pz_data, p_test)
678 if (p_test->p_test_regex == 0)
679 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
680 p_test->pz_test_text);
682 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
688 /* * * * * * * * * * * * *
690 quoted_file_exists Make sure that a file exists before we emit
691 the file name. If we emit the name, our invoking shell will try
692 to copy a non-existing file into the destination directory. */
694 static int quoted_file_exists PARAMS ((const char *, const char *, const char *));
696 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
697 const char *pz_src_path;
698 const char *pz_file_path;
701 char z[ MAXPATHLEN ];
703 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
704 pz = z + strlen ( z );
707 char ch = *pz_file++;
717 if (stat (z, &s) != 0)
719 return S_ISREG( s.st_mode );
724 /* * * * * * * * * * * * *
728 The syntax, `#include "file.h"' specifies that the compiler is to
729 search the local directory of the current file before the include
730 list. Consequently, if we have modified a header and stored it in
731 another directory, any files that are included by that modified
732 file in that fashion must also be copied into this new directory.
733 This routine finds those flavors of #include and for each one found
736 1. source directory of the original file
737 2. the relative path file name of the #includ-ed file
738 3. the full destination path for this file
740 Input: the text of the file, the file name and a pointer to the
741 match list where the match information was stored.
742 Result: internally nothing. The results are written to stdout
743 for interpretation by the invoking shell */
746 static void extract_quoted_files PARAMS ((char *, const char *, regmatch_t *));
748 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
750 const char *pz_fixed_file;
751 regmatch_t *p_re_match;
753 char *pz_dir_end = strrchr (pz_fixed_file, '/');
754 char *pz_incl_quot = pz_data;
756 if (VLEVEL( VERB_APPLIES ))
757 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
759 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
760 If there is none, then it is in our current directory, ".". */
762 if (pz_dir_end == (char *) NULL)
769 pz_incl_quot += p_re_match->rm_so;
771 /* Skip forward to the included file name */
772 while (*pz_incl_quot != '"')
775 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
777 /* Print the source directory and the subdirectory
778 of the file in question. */
779 printf ("%s %s/", pz_src_dir, pz_fixed_file);
780 pz_dir_end = pz_incl_quot;
782 /* Append to the directory the relative path of the desired file */
783 while (*pz_incl_quot != '"')
784 putc (*pz_incl_quot++, stdout);
786 /* Now print the destination directory appended with the
787 relative path of the desired file */
788 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
789 while (*pz_dir_end != '"')
790 putc (*pz_dir_end++, stdout);
796 /* Find the next entry */
797 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
803 /* * * * * * * * * * * * *
805 Somebody wrote a *_fix subroutine that we must call.
807 #ifndef SEPARATE_FIX_PROC
808 static int internal_fix PARAMS ((int, tFixDesc *));
810 internal_fix (read_fd, p_fixd)
818 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
824 pid_t childid = fork();
847 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
850 static int failCt = 0;
851 if ((errno != EAGAIN) || (++failCt > 10))
858 * Close our current stdin and stdout
860 close (STDIN_FILENO);
861 close (STDOUT_FILENO);
865 * Make the fd passed in the stdin, and the write end of
866 * the new pipe become the stdout.
868 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
869 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
871 apply_fix (p_fixd, pz_curr_file);
874 #endif /* !SEPARATE_FIX_PROC */
877 #ifdef SEPARATE_FIX_PROC
879 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
889 if (p_fixd->fd_flags & FD_SUBROUTINE)
891 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
894 + strlen( pz_orig_dir )
895 + sizeof( z_applyfix_prog )
896 + strlen( pz_fix_file )
897 + strlen( pz_file_source )
898 + strlen( pz_temp_file );
900 pz_cmd = (char*)xmalloc( argsize );
902 strcpy( pz_cmd, pz_orig_dir );
903 pz_scan = pz_cmd + strlen( pz_orig_dir );
904 strcpy( pz_scan, z_applyfix_prog );
905 pz_scan += sizeof( z_applyfix_prog ) - 1;
909 * Now add the fix number and file names that may be needed
911 sprintf (pz_scan, "%ld %s %s %s", p_fixd - fixDescList,
912 pz_fix_file, pz_file_source, pz_temp_file);
914 else /* NOT an "internal" fix: */
918 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
919 dst is a temporary file anyway, so we know there's no other
920 file by that name; and DOS's system(3) doesn't mind to
921 clobber existing file in redirection. Besides, with DOS 8+3
922 limited file namespace, we can easily lose if dst already has
923 an extension that is 3 or more characters long.
925 I do not think the 8+3 issue is relevant because all the files
926 we operate on are named "*.h", making 8+2 adequate. Anyway,
927 the following bizarre use of 'cat' only works on DOS boxes.
928 It causes the file to be dropped into a temporary file for
929 'cat' to read (pipes do not work on DOS). */
930 tSCC z_cmd_fmt[] = " %s | cat > %s";
932 /* Don't use positional formatting arguments because some lame-o
933 implementations cannot cope :-(. */
934 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
936 tCC** ppArgs = p_fixd->patch_args;
938 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
939 + strlen( pz_file_source );
944 * Compute the size of the command line. Add lotsa extra space
945 * because some of the args to sed use lotsa single quotes.
946 * (This requires three extra bytes per quote. Here we allow
947 * for up to 8 single quotes for each argument, including the
948 * command name "sed" itself. Nobody will *ever* need more. :)
952 tCC* p_arg = *(ppArgs++);
955 argsize += 24 + strlen( p_arg );
958 /* Estimated buffer size we will need. */
959 pz_scan = pz_cmd = (char*)xmalloc( argsize );
960 /* How much of it do we allot to the program name and its
962 parg_size = argsize - parg_size;
964 ppArgs = p_fixd->patch_args;
967 * Copy the program name, unquoted
970 tCC* pArg = *(ppArgs++);
981 * Copy the program arguments, quoted
985 tCC* pArg = *(ppArgs++);
990 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
991 parg_size - (pz_scan - pz_cmd) );
993 * Make sure we don't overflow the buffer due to sloppy
996 while (pz_scan == (char*)NULL)
998 size_t already_filled = pz_scan_save - pz_cmd;
999 pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
1000 pz_scan_save = pz_scan = pz_cmd + already_filled;
1002 pz_scan = make_raw_shell_str( pz_scan, pArg,
1003 parg_size - (pz_scan - pz_cmd) );
1008 * add the file machinations.
1011 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1013 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1014 pz_temp_file, pz_temp_file, pz_temp_file);
1018 free( (void*)pz_cmd );
1021 /* * * * * * * * * * * * *
1023 This loop should only cycle for 1/2 of one loop.
1024 "chain_open" starts a process that uses "read_fd" as
1025 its stdin and returns the new fd this process will use
1028 #else /* is *NOT* SEPARATE_FIX_PROC */
1029 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1031 start_fixer (read_fd, p_fixd, pz_fix_file)
1039 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1040 return internal_fix (read_fd, p_fixd);
1042 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1043 pz_cmd = (char*)NULL;
1046 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1047 pz_cmd = (char*) xmalloc (strlen (p_fixd->patch_args[2])
1048 + sizeof( z_cmd_fmt )
1049 + strlen( pz_fix_file ));
1050 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1051 pz_cmd_save = p_fixd->patch_args[2];
1052 p_fixd->patch_args[2] = pz_cmd;
1055 /* Start a fix process, handing off the previous read fd for its
1056 stdin and getting a new fd that reads from the fix process' stdout.
1057 We normally will not loop, but we will up to 10 times if we keep
1058 getting "EAGAIN" errors.
1063 static int failCt = 0;
1066 fd = chain_open (read_fd,
1067 (tCC **) p_fixd->patch_args,
1068 (process_chain_head == -1)
1069 ? &process_chain_head : (pid_t *) NULL);
1077 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1080 if ((errno != EAGAIN) || (++failCt > 10))
1081 exit (EXIT_FAILURE);
1085 /* IF we allocated a shell script command,
1086 THEN free it and restore the command format to the fix description */
1087 if (pz_cmd != (char*)NULL)
1089 free ((void*)pz_cmd);
1090 p_fixd->patch_args[2] = pz_cmd_save;
1098 /* * * * * * * * * * * * *
1100 Process the potential fixes for a particular include file.
1101 Input: the original text of the file and the file's name
1102 Result: none. A new file may or may not be created. */
1104 static t_bool fix_applies PARAMS ((tFixDesc *));
1106 fix_applies (p_fixd)
1109 const char *pz_fname = pz_curr_file;
1110 const char *pz_scan = p_fixd->file_list;
1114 # ifdef SEPARATE_FIX_PROC
1116 * There is only one fix that uses a shell script as of this writing.
1117 * I hope to nuke it anyway, it does not apply to DOS and it would
1118 * be painful to implement. Therefore, no "shell" fixes for DOS.
1120 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1123 if (p_fixd->fd_flags & FD_SKIP_TEST)
1127 /* IF there is a file name restriction,
1128 THEN ensure the current file name matches one in the pattern */
1130 if (pz_scan != (char *) NULL)
1134 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1136 name_len = strlen (pz_fname);
1140 pz_scan = strstr (pz_scan + 1, pz_fname);
1141 /* IF we can't match the string at all,
1143 if (pz_scan == (char *) NULL)
1146 /* IF the match is surrounded by the '|' markers,
1147 THEN we found a full match -- time to run the tests */
1149 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1154 /* FOR each test, see if it fails.
1155 IF it does fail, then we go on to the next test */
1157 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1161 switch (p_test->type)
1164 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1166 if (VLEVEL( VERB_EVERYTHING ))
1167 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1168 pz_fname, p_fixd->test_ct - test_ct);
1175 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1177 if (VLEVEL( VERB_EVERYTHING ))
1178 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1179 pz_fname, p_fixd->test_ct - test_ct);
1186 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1188 if (VLEVEL( VERB_EVERYTHING ))
1189 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1190 pz_fname, p_fixd->test_ct - test_ct);
1198 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1201 if (VLEVEL( VERB_EVERYTHING ))
1202 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1203 pz_fname, p_fixd->test_ct - test_ct);
1215 /* * * * * * * * * * * * *
1217 Write out a replacement file */
1219 static void write_replacement PARAMS ((tFixDesc *));
1221 write_replacement (p_fixd)
1224 const char* pz_text = p_fixd->patch_args[0];
1226 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1230 FILE* out_fp = create_file ();
1231 fputs (pz_text, out_fp);
1237 /* * * * * * * * * * * * *
1239 We have work to do. Read back in the output
1240 of the filtering chain. Compare each byte as we read it with
1241 the contents of the original file. As soon as we find any
1242 difference, we will create the output file, write out all
1243 the matched text and then copy any remaining data from the
1244 output of the filter chain.
1246 static void test_for_changes PARAMS ((int));
1248 test_for_changes (read_fd)
1251 FILE *in_fp = fdopen (read_fd, "r");
1252 FILE *out_fp = (FILE *) NULL;
1253 char *pz_cmp = pz_curr_data;
1266 /* IF we are emitting the output
1267 THEN emit this character, too.
1269 if (out_fp != (FILE *) NULL)
1272 /* ELSE if this character does not match the original,
1273 THEN now is the time to start the output.
1275 else if (ch != *pz_cmp)
1277 out_fp = create_file ();
1282 /* IF there are matched data, write the matched part now. */
1283 if (pz_cmp != pz_curr_data)
1284 fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
1286 /* Emit the current unmatching character */
1290 /* ELSE the character matches. Advance the compare ptr */
1294 /* IF we created the output file, ... */
1295 if (out_fp != (FILE *) NULL)
1299 /* Close the file and see if we have to worry about
1300 `#include "file.h"' constructs. */
1302 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1303 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1307 close (read_fd); /* probably redundant, but I'm paranoid */
1311 /* * * * * * * * * * * * *
1313 Process the potential fixes for a particular include file.
1314 Input: the original text of the file and the file's name
1315 Result: none. A new file may or may not be created. */
1320 tFixDesc *p_fixd = fixDescList;
1321 int todo_ct = FIX_COUNT;
1323 # ifndef SEPARATE_FIX_PROC
1324 int num_children = 0;
1325 # else /* is SEPARATE_FIX_PROC */
1326 char* pz_file_source = pz_curr_file;
1329 if (access (pz_curr_file, R_OK) != 0)
1332 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1333 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1334 erno, xstrerror (erno));
1338 pz_curr_data = load_file (pz_curr_file);
1339 if (pz_curr_data == (char *) NULL)
1345 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1346 fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
1348 # ifndef SEPARATE_FIX_PROC
1349 process_chain_head = NOPROCESS;
1351 /* For every fix in our fix list, ... */
1352 for (; todo_ct > 0; p_fixd++, todo_ct--)
1354 if (! fix_applies (p_fixd))
1357 if (VLEVEL( VERB_APPLIES ))
1358 fprintf (stderr, "Applying %-24s to %s\n",
1359 p_fixd->fix_name, pz_curr_file);
1361 if (p_fixd->fd_flags & FD_REPLACEMENT)
1363 write_replacement (p_fixd);
1368 /* IF we do not have a read pointer,
1369 THEN this is the first fix for the current file.
1370 Open the source file. That will be used as stdin for
1371 the first fix. Any subsequent fixes will use the
1372 stdout descriptor of the previous fix for its stdin. */
1376 read_fd = open (pz_curr_file, O_RDONLY);
1379 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1380 xstrerror (errno), pz_curr_file);
1381 exit (EXIT_FAILURE);
1384 /* Ensure we do not get duplicate output */
1389 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1393 /* IF we have a read-back file descriptor,
1394 THEN check for changes and write output if changed. */
1398 test_for_changes (read_fd);
1400 apply_ct += num_children;
1402 /* Wait for child processes created by chain_open()
1403 to avoid leaving zombies. */
1405 wait ((int *) NULL);
1406 } while (--num_children > 0);
1409 # else /* is SEPARATE_FIX_PROC */
1411 for (; todo_ct > 0; p_fixd++, todo_ct--)
1413 if (! fix_applies (p_fixd))
1416 if (VLEVEL( VERB_APPLIES ))
1417 fprintf (stderr, "Applying %-24s to %s\n",
1418 p_fixd->fix_name, pz_curr_file);
1420 if (p_fixd->fd_flags & FD_REPLACEMENT)
1422 write_replacement (p_fixd);
1426 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1427 pz_file_source = pz_temp_file;
1430 read_fd = open (pz_temp_file, O_RDONLY);
1433 if (errno != ENOENT)
1434 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1435 errno, xstrerror (errno), pz_temp_file);
1439 test_for_changes (read_fd);
1440 /* Unlinking a file while it is still open is a Bad Idea on
1443 unlink (pz_temp_file);