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 GCC will search.
5 Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
7 This file is part of GCC.
9 GCC 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 GCC 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 GCC; 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)
31 #if ! defined( SIGCHLD ) && defined( SIGCLD )
32 # define SIGCHLD SIGCLD
34 #ifndef SEPARATE_FIX_PROC
38 /* The contents of this string are not very important. It is mostly
39 just used as part of the "I am alive and working" test. */
41 static const char program_id
[] = "fixincl version 1.1";
43 /* This format will be used at the start of every generated file */
45 static const char z_std_preamble
[] =
46 "/* DO NOT EDIT THIS FILE.\n\n\
47 It has been auto-edited by fixincludes from:\n\n\
49 This had to be done to correct non-standard usages in the\n\
50 original, manufacturer supplied header file. */\n\n";
52 /* Working environment strings. Essentially, invocation 'options'. */
54 #define _ENV_(v,m,n,t) tCC* v = NULL;
58 int find_base_len
= 0;
69 te_verbose verbose_level
= VERB_PROGRESS
;
72 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
73 #define NOT_SILENT VLEVEL(VERB_FIXES)
75 pid_t process_chain_head
= (pid_t
) -1;
77 char* pz_curr_file
; /* name of the current file under test/fix */
78 char* pz_curr_data
; /* original contents of that file */
79 char* pz_temp_file
; /* for DOS, a place to stash the temporary
80 fixed data between system(3) calls */
81 t_bool curr_data_mapped
;
84 size_t ttl_data_size
= 0;
93 const char incl_quote_pat
[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
94 tSCC z_fork_err
[] = "Error %d (%s) starting filter process for %s\n";
95 regex_t incl_quote_re
;
97 static void do_version (void) ATTRIBUTE_NORETURN
;
98 char *load_file (const char *);
99 void run_compiles (void);
100 void initialize (int argc
, char** argv
);
103 /* External Source Code */
107 /* * * * * * * * * * * * * * * * * * *
111 extern int main (int, char **);
113 main (int argc
, char** argv
)
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 ( int argc
, char** argv
)
217 static const char var_not_found
[] =
219 "fixincl ERROR: %s environment variable not defined\n"
221 "fixincl ERROR: %s environment variable not defined\n"
222 "each of these must be defined:\n"
223 # define _ENV_(vv,mm,nn,tt) "\t" nn " - " tt "\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
);
253 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
254 receive the signal. A different setting is inheritable */
255 signal (SIGCHLD
, SIG_DFL
);
258 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
259 v = getenv (var); if (m && (v == NULL)) { \
260 fprintf (stderr, var_not_found, var); \
261 exit (EXIT_FAILURE); } }
267 if (ISDIGIT ( *pz_verbose
))
268 verbose_level
= (te_verbose
)atoi( pz_verbose
);
270 switch (*pz_verbose
) {
273 verbose_level
= VERB_SILENT
; break;
277 verbose_level
= VERB_FIXES
; break;
281 verbose_level
= VERB_APPLIES
; break;
286 verbose_level
= VERB_PROGRESS
; break;
290 verbose_level
= VERB_TESTS
; break;
294 verbose_level
= VERB_EVERYTHING
; break;
296 if (verbose_level
>= VERB_EVERYTHING
) {
297 verbose_level
= VERB_EVERYTHING
;
298 fputs ("fixinc verbosity: EVERYTHING\n", stderr
);
300 while ((pz_find_base
[0] == '.') && (pz_find_base
[1] == '/'))
302 if ((pz_find_base
[0] != '.') || (pz_find_base
[1] != NUL
))
303 find_base_len
= strlen( pz_find_base
);
305 /* Compile all the regular expressions now.
306 That way, it is done only once for the whole run.
310 # ifdef SEPARATE_FIX_PROC
311 /* NULL as the first argument to `tempnam' causes it to DTRT
312 wrt the temporary directory where the file will be created. */
313 pz_temp_file
= tempnam( NULL
, "fxinc" );
316 signal (SIGQUIT
, SIG_IGN
);
318 signal (SIGIOT
, SIG_IGN
);
321 signal (SIGPIPE
, SIG_IGN
);
323 signal (SIGALRM
, SIG_IGN
);
324 signal (SIGTERM
, SIG_IGN
);
327 /* * * * * * * * * * * * *
329 load_file loads all the contents of a file into malloc-ed memory.
330 Its argument is the name of the file to read in; the returned
331 result is the NUL terminated contents of the file. The file
332 is presumed to be an ASCII text file containing no NULs. */
334 load_file ( const char* fname
)
339 if (stat (fname
, &stbf
) != 0)
342 fprintf (stderr
, "error %d (%s) stat-ing %s\n",
343 errno
, xstrerror (errno
), fname
);
344 return (char *) NULL
;
346 if (stbf
.st_size
== 0)
349 /* Make the data map size one larger than the file size for documentation
350 purposes. Truth is that there will be a following NUL character if
351 the file size is not a multiple of the page size. If it is a multiple,
352 then this adjustment sometimes fails anyway. */
353 data_map_size
= stbf
.st_size
+1;
354 data_map_fd
= open (fname
, O_RDONLY
);
355 ttl_data_size
+= data_map_size
-1;
360 fprintf (stderr
, "error %d (%s) opening %s for read\n",
361 errno
, xstrerror (errno
), fname
);
365 #ifdef HAVE_MMAP_FILE
366 curr_data_mapped
= BOOL_TRUE
;
368 /* IF the file size is a multiple of the page size,
369 THEN sometimes you will seg fault trying to access a trailing byte */
370 if ((stbf
.st_size
& (getpagesize()-1)) == 0)
371 res
= (char*)BAD_ADDR
;
373 res
= (char*)mmap ((void*)NULL
, data_map_size
, PROT_READ
,
374 MAP_PRIVATE
, data_map_fd
, 0);
375 if (res
== (char*)BAD_ADDR
)
378 FILE* fp
= fdopen (data_map_fd
, "r");
379 curr_data_mapped
= BOOL_FALSE
;
380 res
= load_file_data (fp
);
388 machine_matches( tFixDesc
* p_fixd
)
390 # ifndef SEPARATE_FIX_PROC
391 tSCC case_fmt
[] = "case %s in\n"; /* 9 bytes, plus string */
393 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
394 tSCC skip
[] = "skip"; /* 4 bytes */
395 tSCC run
[] = "run"; /* 3 bytes */
396 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
398 const char **papz_machs
= p_fixd
->papz_machs
;
400 const char *pz_sep
= "";
403 char cmd_buf
[ MACH_LIST_SIZE_LIMIT
]; /* size lim from fixincl.tpl */
405 /* Start the case statement */
407 sprintf (cmd_buf
, case_fmt
, pz_machine
);
408 pz
= cmd_buf
+ strlen (cmd_buf
);
410 /* Determine if a match means to apply the fix or not apply it */
412 if (p_fixd
->fd_flags
& FD_MACH_IFNOT
)
423 /* Emit all the machine names. If there are more than one,
424 then we will insert " | \\\n" between the names */
428 const char* pz_mach
= *(papz_machs
++);
430 if (pz_mach
== (const char*) NULL
)
432 sprintf (pz
, "%s%s", pz_sep
, pz_mach
);
437 /* Now emit the match and not-match actions and the esac */
439 sprintf (pz
, esac_fmt
, pz_if_true
, pz_if_false
);
442 The result will start either with 's' or 'r'. */
446 pz
= run_shell (cmd_buf
);
451 p_fixd
->fd_flags
|= FD_SKIP_TEST
;
457 # else /* is SEPARATE_FIX_PROC */
458 const char **papz_machs
= p_fixd
->papz_machs
;
459 int invert
= (p_fixd
->fd_flags
& FD_MACH_IFNOT
) != 0;
462 const char* pz_mach
= *(papz_machs
++);
464 if (pz_mach
== (const char*) NULL
)
466 if (strstr (pz_mach
, "dos") != NULL
&& !invert
)
470 p_fixd
->fd_flags
|= FD_SKIP_TEST
;
475 /* * * * * * * * * * * * *
477 run_compiles run all the regexp compiles for all the fixes once.
482 tFixDesc
*p_fixd
= fixDescList
;
483 int fix_ct
= FIX_COUNT
;
484 regex_t
*p_re
= xcalloc (REGEX_COUNT
, sizeof (regex_t
));
486 /* Make sure compile_re does not stumble across invalid data */
488 memset (&incl_quote_re
, '\0', sizeof (regex_t
));
490 compile_re (incl_quote_pat
, &incl_quote_re
, 1,
491 "quoted include", "run_compiles");
493 /* Allow machine name tests to be ignored (testing, mainly) */
495 if (pz_machine
&& ((*pz_machine
== '\0') || (*pz_machine
== '*')))
496 pz_machine
= (char*)NULL
;
498 /* FOR every fixup, ... */
501 tTestDesc
*p_test
= p_fixd
->p_test_desc
;
502 int test_ct
= p_fixd
->test_ct
;
504 /* IF the machine type pointer is not NULL (we are not in test mode)
505 AND this test is for or not done on particular machines
508 if ( (pz_machine
!= NULL
)
509 && (p_fixd
->papz_machs
!= (const char**) NULL
)
510 && ! machine_matches (p_fixd
) )
513 /* FOR every test for the fixup, ... */
515 while (--test_ct
>= 0)
517 switch (p_test
->type
)
521 p_test
->p_test_regex
= p_re
++;
522 compile_re (p_test
->pz_test_text
, p_test
->p_test_regex
, 0,
523 "select test", p_fixd
->fix_name
);
529 while (p_fixd
++, --fix_ct
> 0);
533 /* * * * * * * * * * * * *
535 create_file Create the output modified file.
536 Input: the name of the file to create
537 Returns: a file pointer to the new, open file */
539 #if defined(S_IRUSR) && defined(S_IWUSR) && \
540 defined(S_IRGRP) && defined(S_IROTH)
542 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
544 # define S_IRALL 0644
547 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
548 defined(S_IROTH) && defined(S_IXOTH)
550 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
552 # define S_DIRALL 0755
561 char fname
[MAXPATHLEN
];
563 sprintf (fname
, "%s/%s", pz_dest_dir
, pz_curr_file
+ find_base_len
);
565 fd
= open (fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRALL
);
567 /* We may need to create the directories needed... */
568 if ((fd
< 0) && (errno
== ENOENT
))
570 char *pz_dir
= strchr (fname
+ 1, '/');
573 while (pz_dir
!= (char *) NULL
)
576 if (stat (fname
, &stbf
) < 0)
578 mkdir (fname
, S_IFDIR
| S_DIRALL
);
582 pz_dir
= strchr (pz_dir
+ 1, '/');
585 /* Now, lets try the open again... */
586 fd
= open (fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRALL
);
590 fprintf (stderr
, "Error %d (%s) creating %s\n",
591 errno
, xstrerror (errno
), fname
);
595 fprintf (stderr
, "Fixed: %s\n", pz_curr_file
);
596 pf
= fdopen (fd
, "w");
599 * IF pz_machine is NULL, then we are in some sort of test mode.
600 * Do not insert the current directory name. Use a constant string.
602 fprintf (pf
, z_std_preamble
,
612 /* * * * * * * * * * * * *
614 test_test make sure a shell-style test expression passes.
615 Input: a pointer to the descriptor of the test to run and
616 the name of the file that we might want to fix
617 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
618 shell script we run. */
619 #ifndef SEPARATE_FIX_PROC
621 test_test (tTestDesc
* p_test
, char* 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
674 egrep_test (char* pz_data
, tTestDesc
* p_test
)
677 if (p_test
->p_test_regex
== 0)
678 fprintf (stderr
, "fixincl ERROR RE not compiled: `%s'\n",
679 p_test
->pz_test_text
);
681 if (xregexec (p_test
->p_test_regex
, pz_data
, 0, 0, 0) == 0)
687 /* * * * * * * * * * * * *
689 quoted_file_exists Make sure that a file exists before we emit
690 the file name. If we emit the name, our invoking shell will try
691 to copy a non-existing file into the destination directory. */
694 quoted_file_exists (const char* pz_src_path
,
695 const char* pz_file_path
,
698 char z
[ MAXPATHLEN
];
700 sprintf (z
, "%s/%s/", pz_src_path
, pz_file_path
);
701 pz
= z
+ strlen ( z
);
704 char ch
= *pz_file
++;
714 if (stat (z
, &s
) != 0)
716 return S_ISREG( s
.st_mode
);
721 /* * * * * * * * * * * * *
725 The syntax, `#include "file.h"' specifies that the compiler is to
726 search the local directory of the current file before the include
727 list. Consequently, if we have modified a header and stored it in
728 another directory, any files that are included by that modified
729 file in that fashion must also be copied into this new directory.
730 This routine finds those flavors of #include and for each one found
733 1. source directory of the original file
734 2. the relative path file name of the #includ-ed file
735 3. the full destination path for this file
737 Input: the text of the file, the file name and a pointer to the
738 match list where the match information was stored.
739 Result: internally nothing. The results are written to stdout
740 for interpretation by the invoking shell */
744 extract_quoted_files (char* pz_data
,
745 const char* pz_fixed_file
,
746 regmatch_t
* p_re_match
)
748 char *pz_dir_end
= strrchr (pz_fixed_file
, '/');
749 char *pz_incl_quot
= pz_data
;
751 if (VLEVEL( VERB_APPLIES
))
752 fprintf (stderr
, "Quoted includes in %s\n", pz_fixed_file
);
754 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
755 If there is none, then it is in our current directory, ".". */
757 if (pz_dir_end
== (char *) NULL
)
764 pz_incl_quot
+= p_re_match
->rm_so
;
766 /* Skip forward to the included file name */
767 while (*pz_incl_quot
!= '"')
770 if (quoted_file_exists (pz_src_dir
, pz_fixed_file
, pz_incl_quot
))
772 /* Print the source directory and the subdirectory
773 of the file in question. */
774 printf ("%s %s/", pz_src_dir
, pz_fixed_file
);
775 pz_dir_end
= pz_incl_quot
;
777 /* Append to the directory the relative path of the desired file */
778 while (*pz_incl_quot
!= '"')
779 putc (*pz_incl_quot
++, stdout
);
781 /* Now print the destination directory appended with the
782 relative path of the desired file */
783 printf (" %s/%s/", pz_dest_dir
, pz_fixed_file
);
784 while (*pz_dir_end
!= '"')
785 putc (*pz_dir_end
++, stdout
);
791 /* Find the next entry */
792 if (xregexec (&incl_quote_re
, pz_incl_quot
, 1, p_re_match
, 0) != 0)
798 /* * * * * * * * * * * * *
800 Somebody wrote a *_fix subroutine that we must call.
802 #ifndef SEPARATE_FIX_PROC
804 internal_fix (int read_fd
, tFixDesc
* p_fixd
)
810 fprintf (stderr
, "Error %d on pipe(2) call\n", errno
);
816 pid_t childid
= fork();
839 fprintf (stderr
, z_fork_err
, errno
, xstrerror (errno
),
842 static int failCt
= 0;
843 if ((errno
!= EAGAIN
) || (++failCt
> 10))
850 * Close our current stdin and stdout
852 close (STDIN_FILENO
);
853 close (STDOUT_FILENO
);
857 * Make the fd passed in the stdin, and the write end of
858 * the new pipe become the stdout.
860 fcntl (fd
[1], F_DUPFD
, STDOUT_FILENO
);
861 fcntl (read_fd
, F_DUPFD
, STDIN_FILENO
);
863 apply_fix (p_fixd
, pz_curr_file
);
866 #endif /* !SEPARATE_FIX_PROC */
869 #ifdef SEPARATE_FIX_PROC
871 fix_with_system (tFixDesc
* p_fixd
,
880 if (p_fixd
->fd_flags
& FD_SUBROUTINE
)
882 tSCC z_applyfix_prog
[] = "/fixinc/applyfix";
885 + strlen( pz_orig_dir
)
886 + sizeof( z_applyfix_prog
)
887 + strlen( pz_fix_file
)
888 + strlen( pz_file_source
)
889 + strlen( pz_temp_file
);
891 pz_cmd
= xmalloc (argsize
);
893 strcpy( pz_cmd
, pz_orig_dir
);
894 pz_scan
= pz_cmd
+ strlen( pz_orig_dir
);
895 strcpy( pz_scan
, z_applyfix_prog
);
896 pz_scan
+= sizeof( z_applyfix_prog
) - 1;
900 * Now add the fix number and file names that may be needed
902 sprintf (pz_scan
, "%ld \'%s\' \'%s\' \'%s\'", p_fixd
- fixDescList
,
903 pz_fix_file
, pz_file_source
, pz_temp_file
);
905 else /* NOT an "internal" fix: */
909 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
910 dst is a temporary file anyway, so we know there's no other
911 file by that name; and DOS's system(3) doesn't mind to
912 clobber existing file in redirection. Besides, with DOS 8+3
913 limited file namespace, we can easily lose if dst already has
914 an extension that is 3 or more characters long.
916 I do not think the 8+3 issue is relevant because all the files
917 we operate on are named "*.h", making 8+2 adequate. Anyway,
918 the following bizarre use of 'cat' only works on DOS boxes.
919 It causes the file to be dropped into a temporary file for
920 'cat' to read (pipes do not work on DOS). */
921 tSCC z_cmd_fmt
[] = " \'%s\' | cat > \'%s\'";
923 /* Don't use positional formatting arguments because some lame-o
924 implementations cannot cope :-(. */
925 tSCC z_cmd_fmt
[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
927 tCC
** ppArgs
= p_fixd
->patch_args
;
929 argsize
= sizeof( z_cmd_fmt
) + strlen( pz_temp_file
)
930 + strlen( pz_file_source
);
935 * Compute the size of the command line. Add lotsa extra space
936 * because some of the args to sed use lotsa single quotes.
937 * (This requires three extra bytes per quote. Here we allow
938 * for up to 8 single quotes for each argument, including the
939 * command name "sed" itself. Nobody will *ever* need more. :)
943 tCC
* p_arg
= *(ppArgs
++);
946 argsize
+= 24 + strlen( p_arg
);
949 /* Estimated buffer size we will need. */
950 pz_scan
= pz_cmd
= xmalloc (argsize
);
951 /* How much of it do we allot to the program name and its
953 parg_size
= argsize
- parg_size
;
955 ppArgs
= p_fixd
->patch_args
;
958 * Copy the program name, unquoted
961 tCC
* pArg
= *(ppArgs
++);
972 * Copy the program arguments, quoted
976 tCC
* pArg
= *(ppArgs
++);
981 pz_scan
= make_raw_shell_str( pz_scan_save
= pz_scan
, pArg
,
982 parg_size
- (pz_scan
- pz_cmd
) );
984 * Make sure we don't overflow the buffer due to sloppy
987 while (pz_scan
== (char*)NULL
)
989 size_t already_filled
= pz_scan_save
- pz_cmd
;
990 pz_cmd
= xrealloc (pz_cmd
, argsize
+= 100);
991 pz_scan_save
= pz_scan
= pz_cmd
+ already_filled
;
993 pz_scan
= make_raw_shell_str( pz_scan
, pArg
,
994 parg_size
- (pz_scan
- pz_cmd
) );
999 * add the file machinations.
1002 sprintf (pz_scan
, z_cmd_fmt
, pz_file_source
, pz_temp_file
);
1004 sprintf (pz_scan
, z_cmd_fmt
, pz_file_source
, pz_temp_file
,
1005 pz_temp_file
, pz_temp_file
, pz_temp_file
);
1009 free( (void*)pz_cmd
);
1012 /* * * * * * * * * * * * *
1014 This loop should only cycle for 1/2 of one loop.
1015 "chain_open" starts a process that uses "read_fd" as
1016 its stdin and returns the new fd this process will use
1019 #else /* is *NOT* SEPARATE_FIX_PROC */
1021 start_fixer (int read_fd
, tFixDesc
* p_fixd
, char* pz_fix_file
)
1026 if ((p_fixd
->fd_flags
& FD_SUBROUTINE
) != 0)
1027 return internal_fix (read_fd
, p_fixd
);
1029 if ((p_fixd
->fd_flags
& FD_SHELL_SCRIPT
) == 0)
1030 pz_cmd
= (char*)NULL
;
1033 tSCC z_cmd_fmt
[] = "file='%s'\n%s";
1034 pz_cmd
= xmalloc (strlen (p_fixd
->patch_args
[2])
1035 + sizeof (z_cmd_fmt
) + strlen (pz_fix_file
));
1036 sprintf (pz_cmd
, z_cmd_fmt
, pz_fix_file
, p_fixd
->patch_args
[2]);
1037 pz_cmd_save
= p_fixd
->patch_args
[2];
1038 p_fixd
->patch_args
[2] = pz_cmd
;
1041 /* Start a fix process, handing off the previous read fd for its
1042 stdin and getting a new fd that reads from the fix process' stdout.
1043 We normally will not loop, but we will up to 10 times if we keep
1044 getting "EAGAIN" errors.
1049 static int failCt
= 0;
1052 fd
= chain_open (read_fd
,
1053 (tCC
**) p_fixd
->patch_args
,
1054 (process_chain_head
== -1)
1055 ? &process_chain_head
: (pid_t
*) NULL
);
1063 fprintf (stderr
, z_fork_err
, errno
, xstrerror (errno
),
1066 if ((errno
!= EAGAIN
) || (++failCt
> 10))
1067 exit (EXIT_FAILURE
);
1071 /* IF we allocated a shell script command,
1072 THEN free it and restore the command format to the fix description */
1073 if (pz_cmd
!= (char*)NULL
)
1075 free ((void*)pz_cmd
);
1076 p_fixd
->patch_args
[2] = pz_cmd_save
;
1084 /* * * * * * * * * * * * *
1086 Process the potential fixes for a particular include file.
1087 Input: the original text of the file and the file's name
1088 Result: none. A new file may or may not be created. */
1091 fix_applies (tFixDesc
* p_fixd
)
1093 const char *pz_fname
= pz_curr_file
;
1094 const char *pz_scan
= p_fixd
->file_list
;
1098 # ifdef SEPARATE_FIX_PROC
1100 * There is only one fix that uses a shell script as of this writing.
1101 * I hope to nuke it anyway, it does not apply to DOS and it would
1102 * be painful to implement. Therefore, no "shell" fixes for DOS.
1104 if (p_fixd
->fd_flags
& (FD_SHELL_SCRIPT
| FD_SKIP_TEST
))
1107 if (p_fixd
->fd_flags
& FD_SKIP_TEST
)
1111 /* IF there is a file name restriction,
1112 THEN ensure the current file name matches one in the pattern */
1114 if (pz_scan
!= (char *) NULL
)
1118 while ((pz_fname
[0] == '.') && (pz_fname
[1] == '/'))
1120 name_len
= strlen (pz_fname
);
1124 pz_scan
= strstr (pz_scan
+ 1, pz_fname
);
1125 /* IF we can't match the string at all,
1127 if (pz_scan
== (char *) NULL
)
1130 /* IF the match is surrounded by the '|' markers,
1131 THEN we found a full match -- time to run the tests */
1133 if ((pz_scan
[-1] == '|') && (pz_scan
[name_len
] == '|'))
1138 /* FOR each test, see if it fails.
1139 IF it does fail, then we go on to the next test */
1141 for (p_test
= p_fixd
->p_test_desc
, test_ct
= p_fixd
->test_ct
;
1145 switch (p_test
->type
)
1148 if (test_test (p_test
, pz_curr_file
) != APPLY_FIX
) {
1150 if (VLEVEL( VERB_EVERYTHING
))
1151 fprintf (stderr
, z_failed
, "TEST", p_fixd
->fix_name
,
1152 pz_fname
, p_fixd
->test_ct
- test_ct
);
1159 if (egrep_test (pz_curr_data
, p_test
) != APPLY_FIX
) {
1161 if (VLEVEL( VERB_EVERYTHING
))
1162 fprintf (stderr
, z_failed
, "EGREP", p_fixd
->fix_name
,
1163 pz_fname
, p_fixd
->test_ct
- test_ct
);
1170 if (egrep_test (pz_curr_data
, p_test
) == APPLY_FIX
) {
1172 if (VLEVEL( VERB_EVERYTHING
))
1173 fprintf (stderr
, z_failed
, "NEGREP", p_fixd
->fix_name
,
1174 pz_fname
, p_fixd
->test_ct
- test_ct
);
1182 if (run_test (p_test
->pz_test_text
, pz_curr_file
, pz_curr_data
)
1185 if (VLEVEL( VERB_EVERYTHING
))
1186 fprintf (stderr
, z_failed
, "FTEST", p_fixd
->fix_name
,
1187 pz_fname
, p_fixd
->test_ct
- test_ct
);
1199 /* * * * * * * * * * * * *
1201 Write out a replacement file */
1204 write_replacement (tFixDesc
* p_fixd
)
1206 const char* pz_text
= p_fixd
->patch_args
[0];
1208 if ((pz_text
== (char*)NULL
) || (*pz_text
== NUL
))
1212 FILE* out_fp
= create_file ();
1213 fputs (pz_text
, out_fp
);
1219 /* * * * * * * * * * * * *
1221 We have work to do. Read back in the output
1222 of the filtering chain. Compare each byte as we read it with
1223 the contents of the original file. As soon as we find any
1224 difference, we will create the output file, write out all
1225 the matched text and then copy any remaining data from the
1226 output of the filter chain.
1229 test_for_changes (int read_fd
)
1231 FILE *in_fp
= fdopen (read_fd
, "r");
1232 FILE *out_fp
= (FILE *) NULL
;
1233 unsigned char *pz_cmp
= (unsigned char*)pz_curr_data
;
1245 ch
&= 0xFF; /* all bytes are 8 bits */
1247 /* IF we are emitting the output
1248 THEN emit this character, too.
1250 if (out_fp
!= (FILE *) NULL
)
1253 /* ELSE if this character does not match the original,
1254 THEN now is the time to start the output.
1256 else if (ch
!= *pz_cmp
)
1258 out_fp
= create_file ();
1263 /* IF there are matched data, write the matched part now. */
1264 if ((char*)pz_cmp
!= pz_curr_data
)
1265 fwrite (pz_curr_data
, (size_t)((char*)pz_cmp
- pz_curr_data
),
1268 /* Emit the current unmatching character */
1272 /* ELSE the character matches. Advance the compare ptr */
1276 /* IF we created the output file, ... */
1277 if (out_fp
!= (FILE *) NULL
)
1281 /* Close the file and see if we have to worry about
1282 `#include "file.h"' constructs. */
1284 if (xregexec (&incl_quote_re
, pz_curr_data
, 1, &match
, 0) == 0)
1285 extract_quoted_files (pz_curr_data
, pz_curr_file
, &match
);
1289 close (read_fd
); /* probably redundant, but I'm paranoid */
1293 /* * * * * * * * * * * * *
1295 Process the potential fixes for a particular include file.
1296 Input: the original text of the file and the file's name
1297 Result: none. A new file may or may not be created. */
1302 tFixDesc
*p_fixd
= fixDescList
;
1303 int todo_ct
= FIX_COUNT
;
1305 # ifndef SEPARATE_FIX_PROC
1306 int num_children
= 0;
1307 # else /* is SEPARATE_FIX_PROC */
1308 char* pz_file_source
= pz_curr_file
;
1311 if (access (pz_curr_file
, R_OK
) != 0)
1314 fprintf (stderr
, "Cannot access %s from %s\n\terror %d (%s)\n",
1315 pz_curr_file
, getcwd ((char *) NULL
, MAXPATHLEN
),
1316 erno
, xstrerror (erno
));
1320 pz_curr_data
= load_file (pz_curr_file
);
1321 if (pz_curr_data
== (char *) NULL
)
1327 if (VLEVEL( VERB_PROGRESS
) && have_tty
)
1328 fprintf (stderr
, "%6lu %-50s \r",
1329 (unsigned long) data_map_size
, pz_curr_file
);
1331 # ifndef SEPARATE_FIX_PROC
1332 process_chain_head
= NOPROCESS
;
1334 /* For every fix in our fix list, ... */
1335 for (; todo_ct
> 0; p_fixd
++, todo_ct
--)
1337 if (! fix_applies (p_fixd
))
1340 if (VLEVEL( VERB_APPLIES
))
1341 fprintf (stderr
, "Applying %-24s to %s\n",
1342 p_fixd
->fix_name
, pz_curr_file
);
1344 if (p_fixd
->fd_flags
& FD_REPLACEMENT
)
1346 write_replacement (p_fixd
);
1351 /* IF we do not have a read pointer,
1352 THEN this is the first fix for the current file.
1353 Open the source file. That will be used as stdin for
1354 the first fix. Any subsequent fixes will use the
1355 stdout descriptor of the previous fix for its stdin. */
1359 read_fd
= open (pz_curr_file
, O_RDONLY
);
1362 fprintf (stderr
, "Error %d (%s) opening %s\n", errno
,
1363 xstrerror (errno
), pz_curr_file
);
1364 exit (EXIT_FAILURE
);
1367 /* Ensure we do not get duplicate output */
1372 read_fd
= start_fixer (read_fd
, p_fixd
, pz_curr_file
);
1376 /* IF we have a read-back file descriptor,
1377 THEN check for changes and write output if changed. */
1381 test_for_changes (read_fd
);
1383 apply_ct
+= num_children
;
1385 /* Wait for child processes created by chain_open()
1386 to avoid leaving zombies. */
1388 wait ((int *) NULL
);
1389 } while (--num_children
> 0);
1392 # else /* is SEPARATE_FIX_PROC */
1394 for (; todo_ct
> 0; p_fixd
++, todo_ct
--)
1396 if (! fix_applies (p_fixd
))
1399 if (VLEVEL( VERB_APPLIES
))
1400 fprintf (stderr
, "Applying %-24s to %s\n",
1401 p_fixd
->fix_name
, pz_curr_file
);
1403 if (p_fixd
->fd_flags
& FD_REPLACEMENT
)
1405 write_replacement (p_fixd
);
1409 fix_with_system (p_fixd
, pz_curr_file
, pz_file_source
, pz_temp_file
);
1410 pz_file_source
= pz_temp_file
;
1413 read_fd
= open (pz_temp_file
, O_RDONLY
);
1416 if (errno
!= ENOENT
)
1417 fprintf (stderr
, "error %d (%s) opening output (%s) for read\n",
1418 errno
, xstrerror (errno
), pz_temp_file
);
1422 test_for_changes (read_fd
);
1423 /* Unlinking a file while it is still open is a Bad Idea on
1426 unlink (pz_temp_file
);