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 #if ! defined( SIGCHLD ) && defined( SIGCLD )
33 # define SIGCHLD SIGCLD
35 #ifndef SEPARATE_FIX_PROC
39 /* The contents of this string are not very important. It is mostly
40 just used as part of the "I am alive and working" test. */
42 static const char program_id[] = "fixincl version 1.1";
44 /* This format will be used at the start of every generated file */
46 static const char z_std_preamble[] =
47 "/* DO NOT EDIT THIS FILE.\n\n\
48 It has been auto-edited by fixincludes from:\n\n\
50 This had to be done to correct non-standard usages in the\n\
51 original, manufacturer supplied header file. */\n\n";
53 /* Working environment strings. Essentially, invocation 'options'. */
55 #define _ENV_(v,m,n,t) tCC* v = NULL;
59 int find_base_len = 0;
70 te_verbose verbose_level = VERB_PROGRESS;
73 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
74 #define NOT_SILENT VLEVEL(VERB_FIXES)
76 pid_t process_chain_head = (pid_t) -1;
78 char* pz_curr_file; /* name of the current file under test/fix */
79 char* pz_curr_data; /* original contents of that file */
80 char* pz_temp_file; /* for DOS, a place to stash the temporary
81 fixed data between system(3) calls */
82 t_bool curr_data_mapped;
85 size_t ttl_data_size = 0;
94 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
95 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
96 regex_t incl_quote_re;
98 static void do_version PARAMS((void)) ATTRIBUTE_NORETURN;
99 char *load_file PARAMS((const char *));
100 void run_compiles PARAMS((void));
101 void initialize PARAMS((int argc,char** argv));
102 void process PARAMS((void));
104 /* External Source Code */
108 /* * * * * * * * * * * * * * * * * * *
112 extern int main PARAMS ((int, char **));
120 initialize ( argc, argv );
122 have_tty = isatty (fileno (stderr));
124 /* Before anything else, ensure we can allocate our file name buffer. */
125 file_name_buf = load_file_data (stdin);
127 /* Because of the way server shells work, you have to keep stdin, out
128 and err open so that the proper input file does not get closed
131 freopen ("/dev/null", "r", stdin);
133 if (file_name_buf == (char *) NULL)
135 fputs ("No file names listed for fixing\n", stderr);
143 /* skip to start of name, past any "./" prefixes */
145 while (ISSPACE (*file_name_buf)) file_name_buf++;
146 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
149 /* Check for end of list */
151 if (*file_name_buf == NUL)
154 /* Set global file name pointer and find end of name */
156 pz_curr_file = file_name_buf;
157 pz_end = strchr( pz_curr_file, '\n' );
158 if (pz_end == (char*)NULL)
159 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
161 file_name_buf = pz_end + 1;
163 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
165 /* IF no name is found (blank line) or comment marker, skip line */
167 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
175 if (VLEVEL( VERB_PROGRESS )) {
178 Processed %5d files containing %d bytes \n\
179 Applying %5d fixes to %d files\n\
180 Altering %5d of them\n";
182 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
183 fixed_ct, altered_ct);
185 #endif /* DO_STATS */
187 # ifdef SEPARATE_FIX_PROC
188 unlink( pz_temp_file );
197 static const char zFmt[] = "echo '%s'";
200 /* The 'version' option is really used to test that:
201 1. The program loads correctly (no missing libraries)
202 2. that we can compile all the regular expressions.
203 3. we can correctly run our server shell process
206 sprintf (zBuf, zFmt, program_id);
207 #ifndef SEPARATE_FIX_PROC
209 exit (strcmp (run_shell (zBuf), program_id));
211 exit (system (zBuf));
215 /* * * * * * * * * * * * */
218 initialize ( argc, argv )
222 static const char var_not_found[] =
224 "fixincl ERROR: %s environment variable not defined\n"
226 "fixincl ERROR: %s environment variable not defined\n"
227 "each of these must be defined:\n"
228 # define _ENV_(vv,mm,nn,tt) "\t" nn " - " tt "\n"
234 xmalloc_set_program_name (argv[0]);
242 if (strcmp (argv[1], "-v") == 0)
244 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
246 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
247 errno, xstrerror (errno), argv[1] );
253 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
258 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
259 receive the signal. A different setting is inheritable */
260 signal (SIGCHLD, SIG_DFL);
263 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
264 v = getenv (var); if (m && (v == NULL)) { \
265 fprintf (stderr, var_not_found, var); \
266 exit (EXIT_FAILURE); } }
272 if (ISDIGIT ( *pz_verbose ))
273 verbose_level = (te_verbose)atoi( pz_verbose );
275 switch (*pz_verbose) {
278 verbose_level = VERB_SILENT; break;
282 verbose_level = VERB_FIXES; break;
286 verbose_level = VERB_APPLIES; break;
290 verbose_level = VERB_PROGRESS; break;
294 verbose_level = VERB_TESTS; break;
298 verbose_level = VERB_EVERYTHING; break;
301 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
303 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
304 find_base_len = strlen( pz_find_base );
306 /* Compile all the regular expressions now.
307 That way, it is done only once for the whole run.
311 # ifdef SEPARATE_FIX_PROC
312 /* NULL as the first argument to `tempnam' causes it to DTRT
313 wrt the temporary directory where the file will be created. */
314 pz_temp_file = tempnam( NULL, "fxinc" );
317 signal (SIGQUIT, SIG_IGN);
319 signal (SIGIOT, SIG_IGN);
322 signal (SIGPIPE, SIG_IGN);
324 signal (SIGALRM, SIG_IGN);
325 signal (SIGTERM, SIG_IGN);
328 /* * * * * * * * * * * * *
330 load_file loads all the contents of a file into malloc-ed memory.
331 Its argument is the name of the file to read in; the returned
332 result is the NUL terminated contents of the file. The file
333 is presumed to be an ASCII text file containing no NULs. */
341 if (stat (fname, &stbf) != 0)
344 fprintf (stderr, "error %d (%s) stat-ing %s\n",
345 errno, xstrerror (errno), fname );
346 return (char *) NULL;
348 if (stbf.st_size == 0)
351 /* Make the data map size one larger than the file size for documentation
352 purposes. Truth is that there will be a following NUL character if
353 the file size is not a multiple of the page size. If it is a multiple,
354 then this adjustment sometimes fails anyway. */
355 data_map_size = stbf.st_size+1;
356 data_map_fd = open (fname, O_RDONLY);
357 ttl_data_size += data_map_size-1;
362 fprintf (stderr, "error %d (%s) opening %s for read\n",
363 errno, xstrerror (errno), fname);
367 #ifdef HAVE_MMAP_FILE
368 curr_data_mapped = BOOL_TRUE;
370 /* IF the file size is a multiple of the page size,
371 THEN sometimes you will seg fault trying to access a trailing byte */
372 if ((stbf.st_size & (getpagesize()-1)) == 0)
373 res = (char*)BAD_ADDR;
375 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
376 MAP_PRIVATE, data_map_fd, 0);
377 if (res == (char*)BAD_ADDR)
380 FILE* fp = fdopen (data_map_fd, "r");
381 curr_data_mapped = BOOL_FALSE;
382 res = load_file_data (fp);
389 static int machine_matches PARAMS ((tFixDesc *));
391 machine_matches( p_fixd )
394 # ifndef SEPARATE_FIX_PROC
395 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
397 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
398 tSCC skip[] = "skip"; /* 4 bytes */
399 tSCC run[] = "run"; /* 3 bytes */
400 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
402 const char **papz_machs = p_fixd->papz_machs;
404 const char *pz_sep = "";
407 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
409 /* Start the case statement */
411 sprintf (cmd_buf, case_fmt, pz_machine);
412 pz = cmd_buf + strlen (cmd_buf);
414 /* Determine if a match means to apply the fix or not apply it */
416 if (p_fixd->fd_flags & FD_MACH_IFNOT)
427 /* Emit all the machine names. If there are more than one,
428 then we will insert " | \\\n" between the names */
432 const char* pz_mach = *(papz_machs++);
434 if (pz_mach == (const char*) NULL)
436 sprintf (pz, "%s%s", pz_sep, pz_mach);
441 /* Now emit the match and not-match actions and the esac */
443 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
446 The result will start either with 's' or 'r'. */
450 pz = run_shell (cmd_buf);
455 p_fixd->fd_flags |= FD_SKIP_TEST;
461 # else /* is SEPARATE_FIX_PROC */
462 const char **papz_machs = p_fixd->papz_machs;
463 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
466 const char* pz_mach = *(papz_machs++);
468 if (pz_mach == (const char*) NULL)
470 if (strstr (pz_mach, "dos") != NULL && !invert)
474 p_fixd->fd_flags |= FD_SKIP_TEST;
479 /* * * * * * * * * * * * *
481 run_compiles run all the regexp compiles for all the fixes once.
486 tFixDesc *p_fixd = fixDescList;
487 int fix_ct = FIX_COUNT;
488 regex_t *p_re = (regex_t *) xmalloc (REGEX_COUNT * sizeof (regex_t));
490 /* Make sure compile_re does not stumble across invalid data */
492 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
493 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
495 compile_re (incl_quote_pat, &incl_quote_re, 1,
496 "quoted include", "run_compiles");
498 /* Allow machine name tests to be ignored (testing, mainly) */
500 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
501 pz_machine = (char*)NULL;
503 /* FOR every fixup, ... */
506 tTestDesc *p_test = p_fixd->p_test_desc;
507 int test_ct = p_fixd->test_ct;
509 /* IF the machine type pointer is not NULL (we are not in test mode)
510 AND this test is for or not done on particular machines
513 if ( (pz_machine != NULL)
514 && (p_fixd->papz_machs != (const char**) NULL)
515 && ! machine_matches (p_fixd) )
518 /* FOR every test for the fixup, ... */
520 while (--test_ct >= 0)
522 switch (p_test->type)
526 p_test->p_test_regex = p_re++;
527 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
528 "select test", p_fixd->fix_name);
534 while (p_fixd++, --fix_ct > 0);
538 /* * * * * * * * * * * * *
540 create_file Create the output modified file.
541 Input: the name of the file to create
542 Returns: a file pointer to the new, open file */
544 #if defined(S_IRUSR) && defined(S_IWUSR) && \
545 defined(S_IRGRP) && defined(S_IROTH)
547 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
549 # define S_IRALL 0644
552 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
553 defined(S_IROTH) && defined(S_IXOTH)
555 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
557 # define S_DIRALL 0755
561 static FILE *create_file PARAMS ((void));
567 char fname[MAXPATHLEN];
569 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
571 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
573 /* We may need to create the directories needed... */
574 if ((fd < 0) && (errno == ENOENT))
576 char *pz_dir = strchr (fname + 1, '/');
579 while (pz_dir != (char *) NULL)
582 if (stat (fname, &stbf) < 0)
584 mkdir (fname, S_IFDIR | S_DIRALL);
588 pz_dir = strchr (pz_dir + 1, '/');
591 /* Now, lets try the open again... */
592 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
596 fprintf (stderr, "Error %d (%s) creating %s\n",
597 errno, xstrerror (errno), fname);
601 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
602 pf = fdopen (fd, "w");
605 * IF pz_machine is NULL, then we are in some sort of test mode.
606 * Do not insert the current directory name. Use a constant string.
608 fprintf (pf, z_std_preamble,
618 /* * * * * * * * * * * * *
620 test_test make sure a shell-style test expression passes.
621 Input: a pointer to the descriptor of the test to run and
622 the name of the file that we might want to fix
623 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
624 shell script we run. */
625 #ifndef SEPARATE_FIX_PROC
626 static int test_test PARAMS ((tTestDesc *, char *));
628 test_test (p_test, pz_test_file)
634 if ( test %s ) > /dev/null 2>&1\n\
642 static char cmd_buf[4096];
644 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
645 pz_res = run_shell (cmd_buf);
657 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
661 free ((void *) pz_res);
666 * IF we are in MS-DOS land, then whatever shell-type test is required
667 * will, by definition, fail
669 #define test_test(t,tf) SKIP_FIX
672 /* * * * * * * * * * * * *
674 egrep_test make sure an egrep expression is found in the file text.
675 Input: a pointer to the descriptor of the test to run and
676 the pointer to the contents of the file under suspicion
677 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
679 The caller may choose to reverse meaning if the sense of the test
682 static int egrep_test PARAMS ((char *, tTestDesc *));
684 egrep_test (pz_data, p_test)
689 if (p_test->p_test_regex == 0)
690 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
691 p_test->pz_test_text);
693 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
699 /* * * * * * * * * * * * *
701 quoted_file_exists Make sure that a file exists before we emit
702 the file name. If we emit the name, our invoking shell will try
703 to copy a non-existing file into the destination directory. */
705 static int quoted_file_exists PARAMS ((const char *, const char *, const char *));
707 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
708 const char *pz_src_path;
709 const char *pz_file_path;
712 char z[ MAXPATHLEN ];
714 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
715 pz = z + strlen ( z );
718 char ch = *pz_file++;
728 if (stat (z, &s) != 0)
730 return S_ISREG( s.st_mode );
735 /* * * * * * * * * * * * *
739 The syntax, `#include "file.h"' specifies that the compiler is to
740 search the local directory of the current file before the include
741 list. Consequently, if we have modified a header and stored it in
742 another directory, any files that are included by that modified
743 file in that fashion must also be copied into this new directory.
744 This routine finds those flavors of #include and for each one found
747 1. source directory of the original file
748 2. the relative path file name of the #includ-ed file
749 3. the full destination path for this file
751 Input: the text of the file, the file name and a pointer to the
752 match list where the match information was stored.
753 Result: internally nothing. The results are written to stdout
754 for interpretation by the invoking shell */
757 static void extract_quoted_files PARAMS ((char *, const char *, regmatch_t *));
759 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
761 const char *pz_fixed_file;
762 regmatch_t *p_re_match;
764 char *pz_dir_end = strrchr (pz_fixed_file, '/');
765 char *pz_incl_quot = pz_data;
767 if (VLEVEL( VERB_APPLIES ))
768 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
770 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
771 If there is none, then it is in our current directory, ".". */
773 if (pz_dir_end == (char *) NULL)
780 pz_incl_quot += p_re_match->rm_so;
782 /* Skip forward to the included file name */
783 while (*pz_incl_quot != '"')
786 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
788 /* Print the source directory and the subdirectory
789 of the file in question. */
790 printf ("%s %s/", pz_src_dir, pz_fixed_file);
791 pz_dir_end = pz_incl_quot;
793 /* Append to the directory the relative path of the desired file */
794 while (*pz_incl_quot != '"')
795 putc (*pz_incl_quot++, stdout);
797 /* Now print the destination directory appended with the
798 relative path of the desired file */
799 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
800 while (*pz_dir_end != '"')
801 putc (*pz_dir_end++, stdout);
807 /* Find the next entry */
808 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
814 /* * * * * * * * * * * * *
816 Somebody wrote a *_fix subroutine that we must call.
818 #ifndef SEPARATE_FIX_PROC
819 static int internal_fix PARAMS ((int, tFixDesc *));
821 internal_fix (read_fd, p_fixd)
829 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
835 pid_t childid = fork();
858 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
861 static int failCt = 0;
862 if ((errno != EAGAIN) || (++failCt > 10))
869 * Close our current stdin and stdout
871 close (STDIN_FILENO);
872 close (STDOUT_FILENO);
876 * Make the fd passed in the stdin, and the write end of
877 * the new pipe become the stdout.
879 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
880 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
882 apply_fix (p_fixd, pz_curr_file);
885 #endif /* !SEPARATE_FIX_PROC */
888 #ifdef SEPARATE_FIX_PROC
890 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
900 if (p_fixd->fd_flags & FD_SUBROUTINE)
902 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
905 + strlen( pz_orig_dir )
906 + sizeof( z_applyfix_prog )
907 + strlen( pz_fix_file )
908 + strlen( pz_file_source )
909 + strlen( pz_temp_file );
911 pz_cmd = (char*)xmalloc( argsize );
913 strcpy( pz_cmd, pz_orig_dir );
914 pz_scan = pz_cmd + strlen( pz_orig_dir );
915 strcpy( pz_scan, z_applyfix_prog );
916 pz_scan += sizeof( z_applyfix_prog ) - 1;
920 * Now add the fix number and file names that may be needed
922 sprintf (pz_scan, "%ld \'%s\' \'%s\' \'%s\'", p_fixd - fixDescList,
923 pz_fix_file, pz_file_source, pz_temp_file);
925 else /* NOT an "internal" fix: */
929 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
930 dst is a temporary file anyway, so we know there's no other
931 file by that name; and DOS's system(3) doesn't mind to
932 clobber existing file in redirection. Besides, with DOS 8+3
933 limited file namespace, we can easily lose if dst already has
934 an extension that is 3 or more characters long.
936 I do not think the 8+3 issue is relevant because all the files
937 we operate on are named "*.h", making 8+2 adequate. Anyway,
938 the following bizarre use of 'cat' only works on DOS boxes.
939 It causes the file to be dropped into a temporary file for
940 'cat' to read (pipes do not work on DOS). */
941 tSCC z_cmd_fmt[] = " \'%s\' | cat > \'%s\'";
943 /* Don't use positional formatting arguments because some lame-o
944 implementations cannot cope :-(. */
945 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
947 tCC** ppArgs = p_fixd->patch_args;
949 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
950 + strlen( pz_file_source );
955 * Compute the size of the command line. Add lotsa extra space
956 * because some of the args to sed use lotsa single quotes.
957 * (This requires three extra bytes per quote. Here we allow
958 * for up to 8 single quotes for each argument, including the
959 * command name "sed" itself. Nobody will *ever* need more. :)
963 tCC* p_arg = *(ppArgs++);
966 argsize += 24 + strlen( p_arg );
969 /* Estimated buffer size we will need. */
970 pz_scan = pz_cmd = (char*)xmalloc( argsize );
971 /* How much of it do we allot to the program name and its
973 parg_size = argsize - parg_size;
975 ppArgs = p_fixd->patch_args;
978 * Copy the program name, unquoted
981 tCC* pArg = *(ppArgs++);
992 * Copy the program arguments, quoted
996 tCC* pArg = *(ppArgs++);
1001 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
1002 parg_size - (pz_scan - pz_cmd) );
1004 * Make sure we don't overflow the buffer due to sloppy
1007 while (pz_scan == (char*)NULL)
1009 size_t already_filled = pz_scan_save - pz_cmd;
1010 pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
1011 pz_scan_save = pz_scan = pz_cmd + already_filled;
1013 pz_scan = make_raw_shell_str( pz_scan, pArg,
1014 parg_size - (pz_scan - pz_cmd) );
1019 * add the file machinations.
1022 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1024 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1025 pz_temp_file, pz_temp_file, pz_temp_file);
1029 free( (void*)pz_cmd );
1032 /* * * * * * * * * * * * *
1034 This loop should only cycle for 1/2 of one loop.
1035 "chain_open" starts a process that uses "read_fd" as
1036 its stdin and returns the new fd this process will use
1039 #else /* is *NOT* SEPARATE_FIX_PROC */
1040 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1042 start_fixer (read_fd, p_fixd, pz_fix_file)
1050 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1051 return internal_fix (read_fd, p_fixd);
1053 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1054 pz_cmd = (char*)NULL;
1057 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1058 pz_cmd = (char*) xmalloc (strlen (p_fixd->patch_args[2])
1059 + sizeof( z_cmd_fmt )
1060 + strlen( pz_fix_file ));
1061 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1062 pz_cmd_save = p_fixd->patch_args[2];
1063 p_fixd->patch_args[2] = pz_cmd;
1066 /* Start a fix process, handing off the previous read fd for its
1067 stdin and getting a new fd that reads from the fix process' stdout.
1068 We normally will not loop, but we will up to 10 times if we keep
1069 getting "EAGAIN" errors.
1074 static int failCt = 0;
1077 fd = chain_open (read_fd,
1078 (tCC **) p_fixd->patch_args,
1079 (process_chain_head == -1)
1080 ? &process_chain_head : (pid_t *) NULL);
1088 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1091 if ((errno != EAGAIN) || (++failCt > 10))
1092 exit (EXIT_FAILURE);
1096 /* IF we allocated a shell script command,
1097 THEN free it and restore the command format to the fix description */
1098 if (pz_cmd != (char*)NULL)
1100 free ((void*)pz_cmd);
1101 p_fixd->patch_args[2] = pz_cmd_save;
1109 /* * * * * * * * * * * * *
1111 Process the potential fixes for a particular include file.
1112 Input: the original text of the file and the file's name
1113 Result: none. A new file may or may not be created. */
1115 static t_bool fix_applies PARAMS ((tFixDesc *));
1117 fix_applies (p_fixd)
1120 const char *pz_fname = pz_curr_file;
1121 const char *pz_scan = p_fixd->file_list;
1125 # ifdef SEPARATE_FIX_PROC
1127 * There is only one fix that uses a shell script as of this writing.
1128 * I hope to nuke it anyway, it does not apply to DOS and it would
1129 * be painful to implement. Therefore, no "shell" fixes for DOS.
1131 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1134 if (p_fixd->fd_flags & FD_SKIP_TEST)
1138 /* IF there is a file name restriction,
1139 THEN ensure the current file name matches one in the pattern */
1141 if (pz_scan != (char *) NULL)
1145 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1147 name_len = strlen (pz_fname);
1151 pz_scan = strstr (pz_scan + 1, pz_fname);
1152 /* IF we can't match the string at all,
1154 if (pz_scan == (char *) NULL)
1157 /* IF the match is surrounded by the '|' markers,
1158 THEN we found a full match -- time to run the tests */
1160 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1165 /* FOR each test, see if it fails.
1166 IF it does fail, then we go on to the next test */
1168 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1172 switch (p_test->type)
1175 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1177 if (VLEVEL( VERB_EVERYTHING ))
1178 fprintf (stderr, z_failed, "TEST", 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, "EGREP", p_fixd->fix_name,
1190 pz_fname, p_fixd->test_ct - test_ct);
1197 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1199 if (VLEVEL( VERB_EVERYTHING ))
1200 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1201 pz_fname, p_fixd->test_ct - test_ct);
1209 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1212 if (VLEVEL( VERB_EVERYTHING ))
1213 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1214 pz_fname, p_fixd->test_ct - test_ct);
1226 /* * * * * * * * * * * * *
1228 Write out a replacement file */
1230 static void write_replacement PARAMS ((tFixDesc *));
1232 write_replacement (p_fixd)
1235 const char* pz_text = p_fixd->patch_args[0];
1237 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1241 FILE* out_fp = create_file ();
1242 fputs (pz_text, out_fp);
1248 /* * * * * * * * * * * * *
1250 We have work to do. Read back in the output
1251 of the filtering chain. Compare each byte as we read it with
1252 the contents of the original file. As soon as we find any
1253 difference, we will create the output file, write out all
1254 the matched text and then copy any remaining data from the
1255 output of the filter chain.
1257 static void test_for_changes PARAMS ((int));
1259 test_for_changes (read_fd)
1262 FILE *in_fp = fdopen (read_fd, "r");
1263 FILE *out_fp = (FILE *) NULL;
1264 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1276 ch &= 0xFF; /* all bytes are 8 bits */
1278 /* IF we are emitting the output
1279 THEN emit this character, too.
1281 if (out_fp != (FILE *) NULL)
1284 /* ELSE if this character does not match the original,
1285 THEN now is the time to start the output.
1287 else if (ch != *pz_cmp)
1289 out_fp = create_file ();
1294 /* IF there are matched data, write the matched part now. */
1295 if ((char*)pz_cmp != pz_curr_data)
1296 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
1299 /* Emit the current unmatching character */
1303 /* ELSE the character matches. Advance the compare ptr */
1307 /* IF we created the output file, ... */
1308 if (out_fp != (FILE *) NULL)
1312 /* Close the file and see if we have to worry about
1313 `#include "file.h"' constructs. */
1315 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1316 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1320 close (read_fd); /* probably redundant, but I'm paranoid */
1324 /* * * * * * * * * * * * *
1326 Process the potential fixes for a particular include file.
1327 Input: the original text of the file and the file's name
1328 Result: none. A new file may or may not be created. */
1333 tFixDesc *p_fixd = fixDescList;
1334 int todo_ct = FIX_COUNT;
1336 # ifndef SEPARATE_FIX_PROC
1337 int num_children = 0;
1338 # else /* is SEPARATE_FIX_PROC */
1339 char* pz_file_source = pz_curr_file;
1342 if (access (pz_curr_file, R_OK) != 0)
1345 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1346 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1347 erno, xstrerror (erno));
1351 pz_curr_data = load_file (pz_curr_file);
1352 if (pz_curr_data == (char *) NULL)
1358 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1359 fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
1361 # ifndef SEPARATE_FIX_PROC
1362 process_chain_head = NOPROCESS;
1364 /* For every fix in our fix list, ... */
1365 for (; todo_ct > 0; p_fixd++, todo_ct--)
1367 if (! fix_applies (p_fixd))
1370 if (VLEVEL( VERB_APPLIES ))
1371 fprintf (stderr, "Applying %-24s to %s\n",
1372 p_fixd->fix_name, pz_curr_file);
1374 if (p_fixd->fd_flags & FD_REPLACEMENT)
1376 write_replacement (p_fixd);
1381 /* IF we do not have a read pointer,
1382 THEN this is the first fix for the current file.
1383 Open the source file. That will be used as stdin for
1384 the first fix. Any subsequent fixes will use the
1385 stdout descriptor of the previous fix for its stdin. */
1389 read_fd = open (pz_curr_file, O_RDONLY);
1392 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1393 xstrerror (errno), pz_curr_file);
1394 exit (EXIT_FAILURE);
1397 /* Ensure we do not get duplicate output */
1402 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1406 /* IF we have a read-back file descriptor,
1407 THEN check for changes and write output if changed. */
1411 test_for_changes (read_fd);
1413 apply_ct += num_children;
1415 /* Wait for child processes created by chain_open()
1416 to avoid leaving zombies. */
1418 wait ((int *) NULL);
1419 } while (--num_children > 0);
1422 # else /* is SEPARATE_FIX_PROC */
1424 for (; todo_ct > 0; p_fixd++, todo_ct--)
1426 if (! fix_applies (p_fixd))
1429 if (VLEVEL( VERB_APPLIES ))
1430 fprintf (stderr, "Applying %-24s to %s\n",
1431 p_fixd->fix_name, pz_curr_file);
1433 if (p_fixd->fd_flags & FD_REPLACEMENT)
1435 write_replacement (p_fixd);
1439 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1440 pz_file_source = pz_temp_file;
1443 read_fd = open (pz_temp_file, O_RDONLY);
1446 if (errno != ENOENT)
1447 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1448 errno, xstrerror (errno), pz_temp_file);
1452 test_for_changes (read_fd);
1453 /* Unlinking a file while it is still open is a Bad Idea on
1456 unlink (pz_temp_file);