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, 2000 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. */
27 #if defined( HAVE_MMAP_FILE )
29 #define BAD_ADDR ((void*)-1)
33 #ifndef SEPARATE_FIX_PROC
37 /* The contents of this string are not very important. It is mostly
38 just used as part of the "I am alive and working" test. */
40 static const char program_id[] = "fixincl version 1.1";
42 /* This format will be used at the start of every generated file */
44 static const char z_std_preamble[] =
45 "/* DO NOT EDIT THIS FILE.\n\n\
46 It has been auto-edited by fixincludes from:\n\n\
48 This had to be done to correct non-standard usages in the\n\
49 original, manufacturer supplied header file. */\n\n";
51 /* Working environment strings. Essentially, invocation 'options'. */
53 #define _ENV_(v,m,n,t) tCC* v = NULL;
57 int find_base_len = 0;
68 te_verbose verbose_level = VERB_PROGRESS;
71 #define VLEVEL(l) (verbose_level >= l)
72 #define NOT_SILENT VLEVEL(VERB_FIXES)
74 pid_t process_chain_head = (pid_t) -1;
76 char* pz_curr_file; /* name of the current file under test/fix */
77 char* pz_curr_data; /* original contents of that file */
78 char* pz_temp_file; /* for DOS, a place to stash the temporary
79 fixed data between system(3) calls */
80 t_bool curr_data_mapped;
83 size_t ttl_data_size = 0;
92 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
93 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
94 regex_t incl_quote_re;
96 static void do_version PARAMS((void)) ATTRIBUTE_NORETURN;
97 char *load_file PARAMS((const char *));
98 void run_compiles PARAMS((void));
99 void initialize PARAMS((int argc,char** argv));
100 void process PARAMS((void));
102 /* External Source Code */
106 /* * * * * * * * * * * * * * * * * * *
110 extern int main PARAMS ((int, char **));
118 initialize ( argc, argv );
120 have_tty = isatty (fileno (stderr));
122 /* Before anything else, ensure we can allocate our file name buffer. */
123 file_name_buf = load_file_data (stdin);
125 /* Because of the way server shells work, you have to keep stdin, out
126 and err open so that the proper input file does not get closed
129 freopen ("/dev/null", "r", stdin);
131 if (file_name_buf == (char *) NULL)
133 fputs ("No file names listed for fixing\n", stderr);
141 /* skip to start of name, past any "./" prefixes */
143 while (ISSPACE (*file_name_buf)) file_name_buf++;
144 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
147 /* Check for end of list */
149 if (*file_name_buf == NUL)
152 /* Set global file name pointer and find end of name */
154 pz_curr_file = file_name_buf;
155 pz_end = strchr( pz_curr_file, '\n' );
156 if (pz_end == (char*)NULL)
157 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
159 file_name_buf = pz_end + 1;
161 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
163 /* IF no name is found (blank line) or comment marker, skip line */
165 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
173 if (VLEVEL( VERB_PROGRESS )) {
176 Processed %5d files containing %d bytes \n\
177 Applying %5d fixes to %d files\n\
178 Altering %5d of them\n";
180 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
181 fixed_ct, altered_ct);
183 #endif /* DO_STATS */
185 # ifdef SEPARATE_FIX_PROC
186 unlink( pz_temp_file );
195 static const char zFmt[] = "echo '%s'";
198 /* The 'version' option is really used to test that:
199 1. The program loads correctly (no missing libraries)
200 2. that we can compile all the regular expressions.
201 3. we can correctly run our server shell process
204 sprintf (zBuf, zFmt, program_id);
205 #ifndef SEPARATE_FIX_PROC
207 exit (strcmp (run_shell (zBuf), program_id));
209 exit (system (zBuf));
213 /* * * * * * * * * * * * */
216 initialize ( argc, argv )
220 static const char var_not_found[] =
221 "fixincl ERROR: %s environment variable not defined\n"
223 "each of these must be defined:\n"
224 #define _ENV_(v,m,n,t) "\t" n " - " t "\n"
230 xmalloc_set_program_name (argv[0]);
238 if (strcmp (argv[1], "-v") == 0)
240 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
242 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
243 errno, xstrerror (errno), argv[1] );
249 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
253 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
254 v = getenv (var); if (m && (v == NULL)) { \
255 fprintf (stderr, var_not_found, var); \
256 exit (EXIT_FAILURE); } }
262 if (ISDIGIT ( *pz_verbose ))
263 verbose_level = (te_verbose)atoi( pz_verbose );
265 switch (*pz_verbose) {
268 verbose_level = VERB_SILENT; break;
272 verbose_level = VERB_FIXES; break;
276 verbose_level = VERB_APPLIES; break;
280 verbose_level = VERB_PROGRESS; break;
284 verbose_level = VERB_TESTS; break;
288 verbose_level = VERB_EVERYTHING; break;
291 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
293 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
294 find_base_len = strlen( pz_find_base );
296 /* Compile all the regular expressions now.
297 That way, it is done only once for the whole run.
301 # ifdef SEPARATE_FIX_PROC
302 /* NULL as the first argument to `tempnam' causes it to DTRT
303 wrt the temporary directory where the file will be created. */
304 pz_temp_file = tempnam( NULL, "fxinc" );
307 signal (SIGQUIT, SIG_IGN);
309 signal (SIGIOT, SIG_IGN);
312 signal (SIGPIPE, SIG_IGN);
314 signal (SIGALRM, SIG_IGN);
315 signal (SIGTERM, SIG_IGN);
318 /* * * * * * * * * * * * *
320 load_file loads all the contents of a file into malloc-ed memory.
321 Its argument is the name of the file to read in; the returned
322 result is the NUL terminated contents of the file. The file
323 is presumed to be an ASCII text file containing no NULs. */
331 if (stat (fname, &stbf) != 0)
334 fprintf (stderr, "error %d (%s) stat-ing %s\n",
335 errno, xstrerror (errno), fname );
336 return (char *) NULL;
338 if (stbf.st_size == 0)
341 /* Make the data map size one larger than the file size for documentation
342 purposes. Truth is that there will be a following NUL character if
343 the file size is not a multiple of the page size. If it is a multiple,
344 then this adjustment sometimes fails anyway. */
345 data_map_size = stbf.st_size+1;
346 data_map_fd = open (fname, O_RDONLY);
347 ttl_data_size += data_map_size-1;
352 fprintf (stderr, "error %d (%s) opening %s for read\n",
353 errno, xstrerror (errno), fname);
357 #ifdef HAVE_MMAP_FILE
358 curr_data_mapped = BOOL_TRUE;
360 /* IF the file size is a multiple of the page size,
361 THEN sometimes you will seg fault trying to access a trailing byte */
362 if ((stbf.st_size & (getpagesize()-1)) == 0)
363 res = (char*)BAD_ADDR;
365 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
366 MAP_PRIVATE, data_map_fd, 0);
367 if (res == (char*)BAD_ADDR)
369 curr_data_mapped = BOOL_FALSE;
370 res = load_file_data ( fdopen (data_map_fd, "r"));
373 curr_data_mapped = BOOL_FALSE;
374 res = load_file_data ( fdopen (data_map_fd, "r"));
380 static int machine_matches PARAMS ((tFixDesc *));
382 machine_matches( p_fixd )
385 # ifndef SEPARATE_FIX_PROC
386 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
388 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
389 tSCC skip[] = "skip"; /* 4 bytes */
390 tSCC run[] = "run"; /* 3 bytes */
391 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
393 const char **papz_machs = p_fixd->papz_machs;
395 const char *pz_sep = "";
398 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
400 /* Start the case statement */
402 sprintf (cmd_buf, case_fmt, pz_machine);
403 pz = cmd_buf + strlen (cmd_buf);
405 /* Determine if a match means to apply the fix or not apply it */
407 if (p_fixd->fd_flags & FD_MACH_IFNOT)
418 /* Emit all the machine names. If there are more than one,
419 then we will insert " | \\\n" between the names */
423 const char* pz_mach = *(papz_machs++);
425 if (pz_mach == (const char*) NULL)
427 sprintf (pz, "%s%s", pz_sep, pz_mach);
432 /* Now emit the match and not-match actions and the esac */
434 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
437 The result will start either with 's' or 'r'. */
441 pz = run_shell (cmd_buf);
446 p_fixd->fd_flags |= FD_SKIP_TEST;
452 # else /* is SEPARATE_FIX_PROC */
453 const char **papz_machs = p_fixd->papz_machs;
454 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
457 const char* pz_mach = *(papz_machs++);
459 if (pz_mach == (const char*) NULL)
461 if (strstr (pz_mach, "dos") != NULL && !invert)
465 p_fixd->fd_flags |= FD_SKIP_TEST;
470 /* * * * * * * * * * * * *
472 run_compiles run all the regexp compiles for all the fixes once.
477 tFixDesc *p_fixd = fixDescList;
478 int fix_ct = FIX_COUNT;
479 regex_t *p_re = (regex_t *) xmalloc (REGEX_COUNT * sizeof (regex_t));
481 /* Make sure compile_re does not stumble across invalid data */
483 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
484 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
486 compile_re (incl_quote_pat, &incl_quote_re, 1,
487 "quoted include", "run_compiles");
489 /* Allow machine name tests to be ignored (testing, mainly) */
491 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
492 pz_machine = (char*)NULL;
494 /* FOR every fixup, ... */
497 tTestDesc *p_test = p_fixd->p_test_desc;
498 int test_ct = p_fixd->test_ct;
500 /* IF the machine type pointer is not NULL (we are not in test mode)
501 AND this test is for or not done on particular machines
504 if ( (pz_machine != NULL)
505 && (p_fixd->papz_machs != (const char**) NULL)
506 && ! machine_matches (p_fixd) )
509 /* FOR every test for the fixup, ... */
511 while (--test_ct >= 0)
513 switch (p_test->type)
517 p_test->p_test_regex = p_re++;
518 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
519 "select test", p_fixd->fix_name);
525 while (p_fixd++, --fix_ct > 0);
529 /* * * * * * * * * * * * *
531 create_file Create the output modified file.
532 Input: the name of the file to create
533 Returns: a file pointer to the new, open file */
535 #if defined(S_IRUSR) && defined(S_IWUSR) && \
536 defined(S_IRGRP) && defined(S_IROTH)
538 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
540 # define S_IRALL 0644
543 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
544 defined(S_IROTH) && defined(S_IXOTH)
546 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
548 # define S_DIRALL 0755
552 static FILE *create_file PARAMS ((void));
558 char fname[MAXPATHLEN];
560 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
562 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
564 /* We may need to create the directories needed... */
565 if ((fd < 0) && (errno == ENOENT))
567 char *pz_dir = strchr (fname + 1, '/');
570 while (pz_dir != (char *) NULL)
573 if (stat (fname, &stbf) < 0)
575 mkdir (fname, S_IFDIR | S_DIRALL);
579 pz_dir = strchr (pz_dir + 1, '/');
582 /* Now, lets try the open again... */
583 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
587 fprintf (stderr, "Error %d (%s) creating %s\n",
588 errno, xstrerror (errno), fname);
592 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
593 pf = fdopen (fd, "w");
596 * IF pz_machine is NULL, then we are in some sort of test mode.
597 * Do not insert the current directory name. Use a constant string.
599 fprintf (pf, z_std_preamble,
609 /* * * * * * * * * * * * *
611 test_test make sure a shell-style test expression passes.
612 Input: a pointer to the descriptor of the test to run and
613 the name of the file that we might want to fix
614 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
615 shell script we run. */
616 #ifndef SEPARATE_FIX_PROC
617 static int test_test PARAMS ((tTestDesc *, char *));
619 test_test (p_test, pz_test_file)
625 if ( test %s ) > /dev/null 2>&1\n\
633 static char cmd_buf[4096];
635 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
636 pz_res = run_shell (cmd_buf);
648 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
652 free ((void *) pz_res);
657 * IF we are in MS-DOS land, then whatever shell-type test is required
658 * will, by definition, fail
660 #define test_test(t,tf) SKIP_FIX
663 /* * * * * * * * * * * * *
665 egrep_test make sure an egrep expression is found in the file text.
666 Input: a pointer to the descriptor of the test to run and
667 the pointer to the contents of the file under suspicion
668 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
670 The caller may choose to reverse meaning if the sense of the test
673 static int egrep_test PARAMS ((char *, tTestDesc *));
675 egrep_test (pz_data, p_test)
680 if (p_test->p_test_regex == 0)
681 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
682 p_test->pz_test_text);
684 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
690 /* * * * * * * * * * * * *
692 quoted_file_exists Make sure that a file exists before we emit
693 the file name. If we emit the name, our invoking shell will try
694 to copy a non-existing file into the destination directory. */
696 static int quoted_file_exists PARAMS ((const char *, const char *, const char *));
698 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
699 const char *pz_src_path;
700 const char *pz_file_path;
703 char z[ MAXPATHLEN ];
705 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
706 pz = z + strlen ( z );
709 char ch = *pz_file++;
719 if (stat (z, &s) != 0)
721 return S_ISREG( s.st_mode );
726 /* * * * * * * * * * * * *
730 The syntax, `#include "file.h"' specifies that the compiler is to
731 search the local directory of the current file before the include
732 list. Consequently, if we have modified a header and stored it in
733 another directory, any files that are included by that modified
734 file in that fashion must also be copied into this new directory.
735 This routine finds those flavors of #include and for each one found
738 1. source directory of the original file
739 2. the relative path file name of the #includ-ed file
740 3. the full destination path for this file
742 Input: the text of the file, the file name and a pointer to the
743 match list where the match information was stored.
744 Result: internally nothing. The results are written to stdout
745 for interpretation by the invoking shell */
748 static void extract_quoted_files PARAMS ((char *, const char *, regmatch_t *));
750 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
752 const char *pz_fixed_file;
753 regmatch_t *p_re_match;
755 char *pz_dir_end = strrchr (pz_fixed_file, '/');
756 char *pz_incl_quot = pz_data;
758 if (VLEVEL( VERB_APPLIES ))
759 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
761 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
762 If there is none, then it is in our current directory, ".". */
764 if (pz_dir_end == (char *) NULL)
771 pz_incl_quot += p_re_match->rm_so;
773 /* Skip forward to the included file name */
774 while (*pz_incl_quot != '"')
777 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
779 /* Print the source directory and the subdirectory
780 of the file in question. */
781 printf ("%s %s/", pz_src_dir, pz_fixed_file);
782 pz_dir_end = pz_incl_quot;
784 /* Append to the directory the relative path of the desired file */
785 while (*pz_incl_quot != '"')
786 putc (*pz_incl_quot++, stdout);
788 /* Now print the destination directory appended with the
789 relative path of the desired file */
790 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
791 while (*pz_dir_end != '"')
792 putc (*pz_dir_end++, stdout);
798 /* Find the next entry */
799 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
805 /* * * * * * * * * * * * *
807 Somebody wrote a *_fix subroutine that we must call.
809 #ifndef SEPARATE_FIX_PROC
810 static int internal_fix PARAMS ((int, tFixDesc *));
812 internal_fix (read_fd, p_fixd)
820 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
826 pid_t childid = fork();
849 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
852 static int failCt = 0;
853 if ((errno != EAGAIN) || (++failCt > 10))
860 * Close our current stdin and stdout
862 close (STDIN_FILENO);
863 close (STDOUT_FILENO);
867 * Make the fd passed in the stdin, and the write end of
868 * the new pipe become the stdout.
870 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
871 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
873 apply_fix (p_fixd, pz_curr_file);
876 #endif /* !SEPARATE_FIX_PROC */
879 #ifdef SEPARATE_FIX_PROC
881 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
891 if (p_fixd->fd_flags & FD_SUBROUTINE)
893 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
896 + strlen( pz_orig_dir )
897 + sizeof( z_applyfix_prog )
898 + strlen( pz_fix_file )
899 + strlen( pz_file_source )
900 + strlen( pz_temp_file );
902 pz_cmd = (char*)xmalloc( argsize );
904 strcpy( pz_cmd, pz_orig_dir );
905 pz_scan = pz_cmd + strlen( pz_orig_dir );
906 strcpy( pz_scan, z_applyfix_prog );
907 pz_scan += sizeof( z_applyfix_prog ) - 1;
911 * Now add the fix number and file names that may be needed
913 sprintf (pz_scan, "%ld %s %s %s", p_fixd - fixDescList,
914 pz_fix_file, pz_file_source, pz_temp_file);
916 else /* NOT an "internal" fix: */
920 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
921 dst is a temporary file anyway, so we know there's no other
922 file by that name; and DOS's system(3) doesn't mind to
923 clobber existing file in redirection. Besides, with DOS 8+3
924 limited file namespace, we can easily lose if dst already has
925 an extension that is 3 or more characters long.
927 I do not think the 8+3 issue is relevant because all the files
928 we operate on are named "*.h", making 8+2 adequate. Anyway,
929 the following bizarre use of 'cat' only works on DOS boxes.
930 It causes the file to be dropped into a temporary file for
931 'cat' to read (pipes do not work on DOS). */
932 tSCC z_cmd_fmt[] = " %s | cat > %s";
934 /* Don't use positional formatting arguments because some lame-o
935 implementations cannot cope :-(. */
936 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
938 tCC** ppArgs = p_fixd->patch_args;
940 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
941 + strlen( pz_file_source );
946 * Compute the size of the command line. Add lotsa extra space
947 * because some of the args to sed use lotsa single quotes.
948 * (This requires three extra bytes per quote. Here we allow
949 * for up to 8 single quotes for each argument, including the
950 * command name "sed" itself. Nobody will *ever* need more. :)
954 tCC* p_arg = *(ppArgs++);
957 argsize += 24 + strlen( p_arg );
960 /* Estimated buffer size we will need. */
961 pz_scan = pz_cmd = (char*)xmalloc( argsize );
962 /* How much of it do we allot to the program name and its
964 parg_size = argsize - parg_size;
966 ppArgs = p_fixd->patch_args;
969 * Copy the program name, unquoted
972 tCC* pArg = *(ppArgs++);
983 * Copy the program arguments, quoted
987 tCC* pArg = *(ppArgs++);
992 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
993 parg_size - (pz_scan - pz_cmd) );
995 * Make sure we don't overflow the buffer due to sloppy
998 while (pz_scan == (char*)NULL)
1000 size_t already_filled = pz_scan_save - pz_cmd;
1001 pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
1002 pz_scan_save = pz_scan = pz_cmd + already_filled;
1004 pz_scan = make_raw_shell_str( pz_scan, pArg,
1005 parg_size - (pz_scan - pz_cmd) );
1010 * add the file machinations.
1013 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1015 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1016 pz_temp_file, pz_temp_file, pz_temp_file);
1020 free( (void*)pz_cmd );
1023 /* * * * * * * * * * * * *
1025 This loop should only cycle for 1/2 of one loop.
1026 "chain_open" starts a process that uses "read_fd" as
1027 its stdin and returns the new fd this process will use
1030 #else /* is *NOT* SEPARATE_FIX_PROC */
1031 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1033 start_fixer (read_fd, p_fixd, pz_fix_file)
1041 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1042 return internal_fix (read_fd, p_fixd);
1044 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1045 pz_cmd = (char*)NULL;
1048 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1049 pz_cmd = (char*) xmalloc (strlen (p_fixd->patch_args[2])
1050 + sizeof( z_cmd_fmt )
1051 + strlen( pz_fix_file ));
1052 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1053 pz_cmd_save = p_fixd->patch_args[2];
1054 p_fixd->patch_args[2] = pz_cmd;
1057 /* Start a fix process, handing off the previous read fd for its
1058 stdin and getting a new fd that reads from the fix process' stdout.
1059 We normally will not loop, but we will up to 10 times if we keep
1060 getting "EAGAIN" errors.
1065 static int failCt = 0;
1068 fd = chain_open (read_fd,
1069 (tCC **) p_fixd->patch_args,
1070 (process_chain_head == -1)
1071 ? &process_chain_head : (pid_t *) NULL);
1079 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1082 if ((errno != EAGAIN) || (++failCt > 10))
1083 exit (EXIT_FAILURE);
1087 /* IF we allocated a shell script command,
1088 THEN free it and restore the command format to the fix description */
1089 if (pz_cmd != (char*)NULL)
1091 free ((void*)pz_cmd);
1092 p_fixd->patch_args[2] = pz_cmd_save;
1100 /* * * * * * * * * * * * *
1102 Process the potential fixes for a particular include file.
1103 Input: the original text of the file and the file's name
1104 Result: none. A new file may or may not be created. */
1106 static t_bool fix_applies PARAMS ((tFixDesc *));
1108 fix_applies (p_fixd)
1111 const char *pz_fname = pz_curr_file;
1112 const char *pz_scan = p_fixd->file_list;
1116 # ifdef SEPARATE_FIX_PROC
1118 * There is only one fix that uses a shell script as of this writing.
1119 * I hope to nuke it anyway, it does not apply to DOS and it would
1120 * be painful to implement. Therefore, no "shell" fixes for DOS.
1122 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1125 if (p_fixd->fd_flags & FD_SKIP_TEST)
1129 /* IF there is a file name restriction,
1130 THEN ensure the current file name matches one in the pattern */
1132 if (pz_scan != (char *) NULL)
1136 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1138 name_len = strlen (pz_fname);
1142 pz_scan = strstr (pz_scan + 1, pz_fname);
1143 /* IF we can't match the string at all,
1145 if (pz_scan == (char *) NULL)
1148 /* IF the match is surrounded by the '|' markers,
1149 THEN we found a full match -- time to run the tests */
1151 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1156 /* FOR each test, see if it fails.
1157 IF it does fail, then we go on to the next test */
1159 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1163 switch (p_test->type)
1166 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1168 if (VLEVEL( VERB_EVERYTHING ))
1169 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1170 pz_fname, p_fixd->test_ct - test_ct);
1177 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1179 if (VLEVEL( VERB_EVERYTHING ))
1180 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1181 pz_fname, p_fixd->test_ct - test_ct);
1188 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1190 if (VLEVEL( VERB_EVERYTHING ))
1191 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1192 pz_fname, p_fixd->test_ct - test_ct);
1200 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1203 if (VLEVEL( VERB_EVERYTHING ))
1204 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1205 pz_fname, p_fixd->test_ct - test_ct);
1217 /* * * * * * * * * * * * *
1219 Write out a replacement file */
1221 static void write_replacement PARAMS ((tFixDesc *));
1223 write_replacement (p_fixd)
1226 const char* pz_text = p_fixd->patch_args[0];
1228 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1232 FILE* out_fp = create_file ();
1233 fputs (pz_text, out_fp);
1239 /* * * * * * * * * * * * *
1241 We have work to do. Read back in the output
1242 of the filtering chain. Compare each byte as we read it with
1243 the contents of the original file. As soon as we find any
1244 difference, we will create the output file, write out all
1245 the matched text and then copy any remaining data from the
1246 output of the filter chain.
1248 static void test_for_changes PARAMS ((int));
1250 test_for_changes (read_fd)
1253 FILE *in_fp = fdopen (read_fd, "r");
1254 FILE *out_fp = (FILE *) NULL;
1255 char *pz_cmp = pz_curr_data;
1268 /* IF we are emitting the output
1269 THEN emit this character, too.
1271 if (out_fp != (FILE *) NULL)
1274 /* ELSE if this character does not match the original,
1275 THEN now is the time to start the output.
1277 else if (ch != *pz_cmp)
1279 out_fp = create_file ();
1284 /* IF there are matched data, write the matched part now. */
1285 if (pz_cmp != pz_curr_data)
1286 fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
1288 /* Emit the current unmatching character */
1292 /* ELSE the character matches. Advance the compare ptr */
1296 /* IF we created the output file, ... */
1297 if (out_fp != (FILE *) NULL)
1301 /* Close the file and see if we have to worry about
1302 `#include "file.h"' constructs. */
1304 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1305 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1309 close (read_fd); /* probably redundant, but I'm paranoid */
1313 /* * * * * * * * * * * * *
1315 Process the potential fixes for a particular include file.
1316 Input: the original text of the file and the file's name
1317 Result: none. A new file may or may not be created. */
1322 tFixDesc *p_fixd = fixDescList;
1323 int todo_ct = FIX_COUNT;
1325 # ifndef SEPARATE_FIX_PROC
1326 int num_children = 0;
1327 # else /* is SEPARATE_FIX_PROC */
1328 char* pz_file_source = pz_curr_file;
1331 if (access (pz_curr_file, R_OK) != 0)
1334 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1335 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1336 erno, xstrerror (erno));
1340 pz_curr_data = load_file (pz_curr_file);
1341 if (pz_curr_data == (char *) NULL)
1347 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1348 fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
1350 # ifndef SEPARATE_FIX_PROC
1351 process_chain_head = NOPROCESS;
1353 /* For every fix in our fix list, ... */
1354 for (; todo_ct > 0; p_fixd++, todo_ct--)
1356 if (! fix_applies (p_fixd))
1359 if (VLEVEL( VERB_APPLIES ))
1360 fprintf (stderr, "Applying %-24s to %s\n",
1361 p_fixd->fix_name, pz_curr_file);
1363 if (p_fixd->fd_flags & FD_REPLACEMENT)
1365 write_replacement (p_fixd);
1370 /* IF we do not have a read pointer,
1371 THEN this is the first fix for the current file.
1372 Open the source file. That will be used as stdin for
1373 the first fix. Any subsequent fixes will use the
1374 stdout descriptor of the previous fix for its stdin. */
1378 read_fd = open (pz_curr_file, O_RDONLY);
1381 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1382 xstrerror (errno), pz_curr_file);
1383 exit (EXIT_FAILURE);
1386 /* Ensure we do not get duplicate output */
1391 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1395 /* IF we have a read-back file descriptor,
1396 THEN check for changes and write output if changed. */
1400 test_for_changes (read_fd);
1402 apply_ct += num_children;
1404 /* Wait for child processes created by chain_open()
1405 to avoid leaving zombies. */
1407 wait ((int *) NULL);
1408 } while (--num_children > 0);
1411 # else /* is SEPARATE_FIX_PROC */
1413 for (; todo_ct > 0; p_fixd++, todo_ct--)
1415 if (! fix_applies (p_fixd))
1418 if (VLEVEL( VERB_APPLIES ))
1419 fprintf (stderr, "Applying %-24s to %s\n",
1420 p_fixd->fix_name, pz_curr_file);
1422 if (p_fixd->fd_flags & FD_REPLACEMENT)
1424 write_replacement (p_fixd);
1428 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1429 pz_file_source = pz_temp_file;
1432 read_fd = open (pz_temp_file, O_RDONLY);
1435 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1436 errno, xstrerror (errno), pz_temp_file);
1440 test_for_changes (read_fd);
1441 /* Unlinking a file while it is still open is a Bad Idea on
1444 unlink (pz_temp_file);