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)
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
PARAMS((void)) ATTRIBUTE_NORETURN
;
98 char *load_file
PARAMS((const char *));
99 void run_compiles
PARAMS((void));
100 void initialize
PARAMS((int argc
,char** argv
));
101 void process
PARAMS((void));
103 /* External Source Code */
107 /* * * * * * * * * * * * * * * * * * *
111 extern int main
PARAMS ((int, char **));
119 initialize ( argc
, argv
);
121 have_tty
= isatty (fileno (stderr
));
123 /* Before anything else, ensure we can allocate our file name buffer. */
124 file_name_buf
= load_file_data (stdin
);
126 /* Because of the way server shells work, you have to keep stdin, out
127 and err open so that the proper input file does not get closed
130 freopen ("/dev/null", "r", stdin
);
132 if (file_name_buf
== (char *) NULL
)
134 fputs ("No file names listed for fixing\n", stderr
);
142 /* skip to start of name, past any "./" prefixes */
144 while (ISSPACE (*file_name_buf
)) file_name_buf
++;
145 while ((file_name_buf
[0] == '.') && (file_name_buf
[1] == '/'))
148 /* Check for end of list */
150 if (*file_name_buf
== NUL
)
153 /* Set global file name pointer and find end of name */
155 pz_curr_file
= file_name_buf
;
156 pz_end
= strchr( pz_curr_file
, '\n' );
157 if (pz_end
== (char*)NULL
)
158 pz_end
= file_name_buf
= pz_curr_file
+ strlen (pz_curr_file
);
160 file_name_buf
= pz_end
+ 1;
162 while ((pz_end
> pz_curr_file
) && ISSPACE( pz_end
[-1])) pz_end
--;
164 /* IF no name is found (blank line) or comment marker, skip line */
166 if ((pz_curr_file
== pz_end
) || (*pz_curr_file
== '#'))
174 if (VLEVEL( VERB_PROGRESS
)) {
177 Processed %5d files containing %d bytes \n\
178 Applying %5d fixes to %d files\n\
179 Altering %5d of them\n";
181 fprintf (stderr
, zFmt
, process_ct
, ttl_data_size
, apply_ct
,
182 fixed_ct
, altered_ct
);
184 #endif /* DO_STATS */
186 # ifdef SEPARATE_FIX_PROC
187 unlink( pz_temp_file
);
196 static const char zFmt
[] = "echo '%s'";
199 /* The 'version' option is really used to test that:
200 1. The program loads correctly (no missing libraries)
201 2. that we can compile all the regular expressions.
202 3. we can correctly run our server shell process
205 sprintf (zBuf
, zFmt
, program_id
);
206 #ifndef SEPARATE_FIX_PROC
208 exit (strcmp (run_shell (zBuf
), program_id
));
210 exit (system (zBuf
));
214 /* * * * * * * * * * * * */
217 initialize ( argc
, argv
)
221 static const char var_not_found
[] =
223 "fixincl ERROR: %s environment variable not defined\n"
225 "fixincl ERROR: %s environment variable not defined\n"
226 "each of these must be defined:\n"
227 # define _ENV_(vv,mm,nn,tt) "\t" nn " - " tt "\n"
233 xmalloc_set_program_name (argv
[0]);
241 if (strcmp (argv
[1], "-v") == 0)
243 if (freopen (argv
[1], "r", stdin
) == (FILE*)NULL
)
245 fprintf (stderr
, "Error %d (%s) reopening %s as stdin\n",
246 errno
, xstrerror (errno
), argv
[1] );
252 fputs ("fixincl ERROR: too many command line arguments\n", stderr
);
257 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
258 receive the signal. A different setting is inheritable */
259 signal (SIGCHLD
, SIG_DFL
);
262 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
263 v = getenv (var); if (m && (v == NULL)) { \
264 fprintf (stderr, var_not_found, var); \
265 exit (EXIT_FAILURE); } }
271 if (ISDIGIT ( *pz_verbose
))
272 verbose_level
= (te_verbose
)atoi( pz_verbose
);
274 switch (*pz_verbose
) {
277 verbose_level
= VERB_SILENT
; break;
281 verbose_level
= VERB_FIXES
; break;
285 verbose_level
= VERB_APPLIES
; break;
289 verbose_level
= VERB_PROGRESS
; break;
293 verbose_level
= VERB_TESTS
; break;
297 verbose_level
= VERB_EVERYTHING
; break;
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. */
340 if (stat (fname
, &stbf
) != 0)
343 fprintf (stderr
, "error %d (%s) stat-ing %s\n",
344 errno
, xstrerror (errno
), fname
);
345 return (char *) NULL
;
347 if (stbf
.st_size
== 0)
350 /* Make the data map size one larger than the file size for documentation
351 purposes. Truth is that there will be a following NUL character if
352 the file size is not a multiple of the page size. If it is a multiple,
353 then this adjustment sometimes fails anyway. */
354 data_map_size
= stbf
.st_size
+1;
355 data_map_fd
= open (fname
, O_RDONLY
);
356 ttl_data_size
+= data_map_size
-1;
361 fprintf (stderr
, "error %d (%s) opening %s for read\n",
362 errno
, xstrerror (errno
), fname
);
366 #ifdef HAVE_MMAP_FILE
367 curr_data_mapped
= BOOL_TRUE
;
369 /* IF the file size is a multiple of the page size,
370 THEN sometimes you will seg fault trying to access a trailing byte */
371 if ((stbf
.st_size
& (getpagesize()-1)) == 0)
372 res
= (char*)BAD_ADDR
;
374 res
= (char*)mmap ((void*)NULL
, data_map_size
, PROT_READ
,
375 MAP_PRIVATE
, data_map_fd
, 0);
376 if (res
== (char*)BAD_ADDR
)
379 FILE* fp
= fdopen (data_map_fd
, "r");
380 curr_data_mapped
= BOOL_FALSE
;
381 res
= load_file_data (fp
);
388 static int machine_matches
PARAMS ((tFixDesc
*));
390 machine_matches( p_fixd
)
393 # ifndef SEPARATE_FIX_PROC
394 tSCC case_fmt
[] = "case %s in\n"; /* 9 bytes, plus string */
396 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
397 tSCC skip
[] = "skip"; /* 4 bytes */
398 tSCC run
[] = "run"; /* 3 bytes */
399 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
401 const char **papz_machs
= p_fixd
->papz_machs
;
403 const char *pz_sep
= "";
406 char cmd_buf
[ MACH_LIST_SIZE_LIMIT
]; /* size lim from fixincl.tpl */
408 /* Start the case statement */
410 sprintf (cmd_buf
, case_fmt
, pz_machine
);
411 pz
= cmd_buf
+ strlen (cmd_buf
);
413 /* Determine if a match means to apply the fix or not apply it */
415 if (p_fixd
->fd_flags
& FD_MACH_IFNOT
)
426 /* Emit all the machine names. If there are more than one,
427 then we will insert " | \\\n" between the names */
431 const char* pz_mach
= *(papz_machs
++);
433 if (pz_mach
== (const char*) NULL
)
435 sprintf (pz
, "%s%s", pz_sep
, pz_mach
);
440 /* Now emit the match and not-match actions and the esac */
442 sprintf (pz
, esac_fmt
, pz_if_true
, pz_if_false
);
445 The result will start either with 's' or 'r'. */
449 pz
= run_shell (cmd_buf
);
454 p_fixd
->fd_flags
|= FD_SKIP_TEST
;
460 # else /* is SEPARATE_FIX_PROC */
461 const char **papz_machs
= p_fixd
->papz_machs
;
462 int invert
= (p_fixd
->fd_flags
& FD_MACH_IFNOT
) != 0;
465 const char* pz_mach
= *(papz_machs
++);
467 if (pz_mach
== (const char*) NULL
)
469 if (strstr (pz_mach
, "dos") != NULL
&& !invert
)
473 p_fixd
->fd_flags
|= FD_SKIP_TEST
;
478 /* * * * * * * * * * * * *
480 run_compiles run all the regexp compiles for all the fixes once.
485 tFixDesc
*p_fixd
= fixDescList
;
486 int fix_ct
= FIX_COUNT
;
487 regex_t
*p_re
= (regex_t
*) xmalloc (REGEX_COUNT
* sizeof (regex_t
));
489 /* Make sure compile_re does not stumble across invalid data */
491 memset ( (void*)p_re
, '\0', REGEX_COUNT
* sizeof (regex_t
) );
492 memset ( (void*)&incl_quote_re
, '\0', sizeof (regex_t
) );
494 compile_re (incl_quote_pat
, &incl_quote_re
, 1,
495 "quoted include", "run_compiles");
497 /* Allow machine name tests to be ignored (testing, mainly) */
499 if (pz_machine
&& ((*pz_machine
== '\0') || (*pz_machine
== '*')))
500 pz_machine
= (char*)NULL
;
502 /* FOR every fixup, ... */
505 tTestDesc
*p_test
= p_fixd
->p_test_desc
;
506 int test_ct
= p_fixd
->test_ct
;
508 /* IF the machine type pointer is not NULL (we are not in test mode)
509 AND this test is for or not done on particular machines
512 if ( (pz_machine
!= NULL
)
513 && (p_fixd
->papz_machs
!= (const char**) NULL
)
514 && ! machine_matches (p_fixd
) )
517 /* FOR every test for the fixup, ... */
519 while (--test_ct
>= 0)
521 switch (p_test
->type
)
525 p_test
->p_test_regex
= p_re
++;
526 compile_re (p_test
->pz_test_text
, p_test
->p_test_regex
, 0,
527 "select test", p_fixd
->fix_name
);
533 while (p_fixd
++, --fix_ct
> 0);
537 /* * * * * * * * * * * * *
539 create_file Create the output modified file.
540 Input: the name of the file to create
541 Returns: a file pointer to the new, open file */
543 #if defined(S_IRUSR) && defined(S_IWUSR) && \
544 defined(S_IRGRP) && defined(S_IROTH)
546 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
548 # define S_IRALL 0644
551 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
552 defined(S_IROTH) && defined(S_IXOTH)
554 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
556 # define S_DIRALL 0755
560 static FILE *create_file
PARAMS ((void));
566 char fname
[MAXPATHLEN
];
568 sprintf (fname
, "%s/%s", pz_dest_dir
, pz_curr_file
+ find_base_len
);
570 fd
= open (fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRALL
);
572 /* We may need to create the directories needed... */
573 if ((fd
< 0) && (errno
== ENOENT
))
575 char *pz_dir
= strchr (fname
+ 1, '/');
578 while (pz_dir
!= (char *) NULL
)
581 if (stat (fname
, &stbf
) < 0)
583 mkdir (fname
, S_IFDIR
| S_DIRALL
);
587 pz_dir
= strchr (pz_dir
+ 1, '/');
590 /* Now, lets try the open again... */
591 fd
= open (fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRALL
);
595 fprintf (stderr
, "Error %d (%s) creating %s\n",
596 errno
, xstrerror (errno
), fname
);
600 fprintf (stderr
, "Fixed: %s\n", pz_curr_file
);
601 pf
= fdopen (fd
, "w");
604 * IF pz_machine is NULL, then we are in some sort of test mode.
605 * Do not insert the current directory name. Use a constant string.
607 fprintf (pf
, z_std_preamble
,
617 /* * * * * * * * * * * * *
619 test_test make sure a shell-style test expression passes.
620 Input: a pointer to the descriptor of the test to run and
621 the name of the file that we might want to fix
622 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
623 shell script we run. */
624 #ifndef SEPARATE_FIX_PROC
625 static int test_test
PARAMS ((tTestDesc
*, char *));
627 test_test (p_test
, pz_test_file
)
633 if ( test %s ) > /dev/null 2>&1\n\
641 static char cmd_buf
[4096];
643 sprintf (cmd_buf
, cmd_fmt
, pz_test_file
, p_test
->pz_test_text
);
644 pz_res
= run_shell (cmd_buf
);
656 fprintf (stderr
, "Script yielded bogus result of `%s':\n%s\n\n",
660 free ((void *) pz_res
);
665 * IF we are in MS-DOS land, then whatever shell-type test is required
666 * will, by definition, fail
668 #define test_test(t,tf) SKIP_FIX
671 /* * * * * * * * * * * * *
673 egrep_test make sure an egrep expression is found in the file text.
674 Input: a pointer to the descriptor of the test to run and
675 the pointer to the contents of the file under suspicion
676 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
678 The caller may choose to reverse meaning if the sense of the test
681 static int egrep_test
PARAMS ((char *, tTestDesc
*));
683 egrep_test (pz_data
, p_test
)
688 if (p_test
->p_test_regex
== 0)
689 fprintf (stderr
, "fixincl ERROR RE not compiled: `%s'\n",
690 p_test
->pz_test_text
);
692 if (regexec (p_test
->p_test_regex
, pz_data
, 0, 0, 0) == 0)
698 /* * * * * * * * * * * * *
700 quoted_file_exists Make sure that a file exists before we emit
701 the file name. If we emit the name, our invoking shell will try
702 to copy a non-existing file into the destination directory. */
704 static int quoted_file_exists
PARAMS ((const char *, const char *, const char *));
706 quoted_file_exists (pz_src_path
, pz_file_path
, pz_file
)
707 const char *pz_src_path
;
708 const char *pz_file_path
;
711 char z
[ MAXPATHLEN
];
713 sprintf (z
, "%s/%s/", pz_src_path
, pz_file_path
);
714 pz
= z
+ strlen ( z
);
717 char ch
= *pz_file
++;
727 if (stat (z
, &s
) != 0)
729 return S_ISREG( s
.st_mode
);
734 /* * * * * * * * * * * * *
738 The syntax, `#include "file.h"' specifies that the compiler is to
739 search the local directory of the current file before the include
740 list. Consequently, if we have modified a header and stored it in
741 another directory, any files that are included by that modified
742 file in that fashion must also be copied into this new directory.
743 This routine finds those flavors of #include and for each one found
746 1. source directory of the original file
747 2. the relative path file name of the #includ-ed file
748 3. the full destination path for this file
750 Input: the text of the file, the file name and a pointer to the
751 match list where the match information was stored.
752 Result: internally nothing. The results are written to stdout
753 for interpretation by the invoking shell */
756 static void extract_quoted_files
PARAMS ((char *, const char *, regmatch_t
*));
758 extract_quoted_files (pz_data
, pz_fixed_file
, p_re_match
)
760 const char *pz_fixed_file
;
761 regmatch_t
*p_re_match
;
763 char *pz_dir_end
= strrchr (pz_fixed_file
, '/');
764 char *pz_incl_quot
= pz_data
;
766 if (VLEVEL( VERB_APPLIES
))
767 fprintf (stderr
, "Quoted includes in %s\n", pz_fixed_file
);
769 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
770 If there is none, then it is in our current directory, ".". */
772 if (pz_dir_end
== (char *) NULL
)
779 pz_incl_quot
+= p_re_match
->rm_so
;
781 /* Skip forward to the included file name */
782 while (*pz_incl_quot
!= '"')
785 if (quoted_file_exists (pz_src_dir
, pz_fixed_file
, pz_incl_quot
))
787 /* Print the source directory and the subdirectory
788 of the file in question. */
789 printf ("%s %s/", pz_src_dir
, pz_fixed_file
);
790 pz_dir_end
= pz_incl_quot
;
792 /* Append to the directory the relative path of the desired file */
793 while (*pz_incl_quot
!= '"')
794 putc (*pz_incl_quot
++, stdout
);
796 /* Now print the destination directory appended with the
797 relative path of the desired file */
798 printf (" %s/%s/", pz_dest_dir
, pz_fixed_file
);
799 while (*pz_dir_end
!= '"')
800 putc (*pz_dir_end
++, stdout
);
806 /* Find the next entry */
807 if (regexec (&incl_quote_re
, pz_incl_quot
, 1, p_re_match
, 0) != 0)
813 /* * * * * * * * * * * * *
815 Somebody wrote a *_fix subroutine that we must call.
817 #ifndef SEPARATE_FIX_PROC
818 static int internal_fix
PARAMS ((int, tFixDesc
*));
820 internal_fix (read_fd
, p_fixd
)
828 fprintf (stderr
, "Error %d on pipe(2) call\n", errno
);
834 pid_t childid
= fork();
857 fprintf (stderr
, z_fork_err
, errno
, xstrerror (errno
),
860 static int failCt
= 0;
861 if ((errno
!= EAGAIN
) || (++failCt
> 10))
868 * Close our current stdin and stdout
870 close (STDIN_FILENO
);
871 close (STDOUT_FILENO
);
875 * Make the fd passed in the stdin, and the write end of
876 * the new pipe become the stdout.
878 fcntl (fd
[1], F_DUPFD
, STDOUT_FILENO
);
879 fcntl (read_fd
, F_DUPFD
, STDIN_FILENO
);
881 apply_fix (p_fixd
, pz_curr_file
);
884 #endif /* !SEPARATE_FIX_PROC */
887 #ifdef SEPARATE_FIX_PROC
889 fix_with_system (p_fixd
, pz_fix_file
, pz_file_source
, pz_temp_file
)
899 if (p_fixd
->fd_flags
& FD_SUBROUTINE
)
901 tSCC z_applyfix_prog
[] = "/fixinc/applyfix";
904 + strlen( pz_orig_dir
)
905 + sizeof( z_applyfix_prog
)
906 + strlen( pz_fix_file
)
907 + strlen( pz_file_source
)
908 + strlen( pz_temp_file
);
910 pz_cmd
= (char*)xmalloc( argsize
);
912 strcpy( pz_cmd
, pz_orig_dir
);
913 pz_scan
= pz_cmd
+ strlen( pz_orig_dir
);
914 strcpy( pz_scan
, z_applyfix_prog
);
915 pz_scan
+= sizeof( z_applyfix_prog
) - 1;
919 * Now add the fix number and file names that may be needed
921 sprintf (pz_scan
, "%ld \'%s\' \'%s\' \'%s\'", p_fixd
- fixDescList
,
922 pz_fix_file
, pz_file_source
, pz_temp_file
);
924 else /* NOT an "internal" fix: */
928 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
929 dst is a temporary file anyway, so we know there's no other
930 file by that name; and DOS's system(3) doesn't mind to
931 clobber existing file in redirection. Besides, with DOS 8+3
932 limited file namespace, we can easily lose if dst already has
933 an extension that is 3 or more characters long.
935 I do not think the 8+3 issue is relevant because all the files
936 we operate on are named "*.h", making 8+2 adequate. Anyway,
937 the following bizarre use of 'cat' only works on DOS boxes.
938 It causes the file to be dropped into a temporary file for
939 'cat' to read (pipes do not work on DOS). */
940 tSCC z_cmd_fmt
[] = " \'%s\' | cat > \'%s\'";
942 /* Don't use positional formatting arguments because some lame-o
943 implementations cannot cope :-(. */
944 tSCC z_cmd_fmt
[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
946 tCC
** ppArgs
= p_fixd
->patch_args
;
948 argsize
= sizeof( z_cmd_fmt
) + strlen( pz_temp_file
)
949 + strlen( pz_file_source
);
954 * Compute the size of the command line. Add lotsa extra space
955 * because some of the args to sed use lotsa single quotes.
956 * (This requires three extra bytes per quote. Here we allow
957 * for up to 8 single quotes for each argument, including the
958 * command name "sed" itself. Nobody will *ever* need more. :)
962 tCC
* p_arg
= *(ppArgs
++);
965 argsize
+= 24 + strlen( p_arg
);
968 /* Estimated buffer size we will need. */
969 pz_scan
= pz_cmd
= (char*)xmalloc( argsize
);
970 /* How much of it do we allot to the program name and its
972 parg_size
= argsize
- parg_size
;
974 ppArgs
= p_fixd
->patch_args
;
977 * Copy the program name, unquoted
980 tCC
* pArg
= *(ppArgs
++);
991 * Copy the program arguments, quoted
995 tCC
* pArg
= *(ppArgs
++);
1000 pz_scan
= make_raw_shell_str( pz_scan_save
= pz_scan
, pArg
,
1001 parg_size
- (pz_scan
- pz_cmd
) );
1003 * Make sure we don't overflow the buffer due to sloppy
1006 while (pz_scan
== (char*)NULL
)
1008 size_t already_filled
= pz_scan_save
- pz_cmd
;
1009 pz_cmd
= (char*)xrealloc( pz_cmd
, argsize
+= 100 );
1010 pz_scan_save
= pz_scan
= pz_cmd
+ already_filled
;
1012 pz_scan
= make_raw_shell_str( pz_scan
, pArg
,
1013 parg_size
- (pz_scan
- pz_cmd
) );
1018 * add the file machinations.
1021 sprintf (pz_scan
, z_cmd_fmt
, pz_file_source
, pz_temp_file
);
1023 sprintf (pz_scan
, z_cmd_fmt
, pz_file_source
, pz_temp_file
,
1024 pz_temp_file
, pz_temp_file
, pz_temp_file
);
1028 free( (void*)pz_cmd
);
1031 /* * * * * * * * * * * * *
1033 This loop should only cycle for 1/2 of one loop.
1034 "chain_open" starts a process that uses "read_fd" as
1035 its stdin and returns the new fd this process will use
1038 #else /* is *NOT* SEPARATE_FIX_PROC */
1039 static int start_fixer
PARAMS ((int, tFixDesc
*, char *));
1041 start_fixer (read_fd
, p_fixd
, pz_fix_file
)
1049 if ((p_fixd
->fd_flags
& FD_SUBROUTINE
) != 0)
1050 return internal_fix (read_fd
, p_fixd
);
1052 if ((p_fixd
->fd_flags
& FD_SHELL_SCRIPT
) == 0)
1053 pz_cmd
= (char*)NULL
;
1056 tSCC z_cmd_fmt
[] = "file='%s'\n%s";
1057 pz_cmd
= (char*) xmalloc (strlen (p_fixd
->patch_args
[2])
1058 + sizeof( z_cmd_fmt
)
1059 + strlen( pz_fix_file
));
1060 sprintf (pz_cmd
, z_cmd_fmt
, pz_fix_file
, p_fixd
->patch_args
[2]);
1061 pz_cmd_save
= p_fixd
->patch_args
[2];
1062 p_fixd
->patch_args
[2] = pz_cmd
;
1065 /* Start a fix process, handing off the previous read fd for its
1066 stdin and getting a new fd that reads from the fix process' stdout.
1067 We normally will not loop, but we will up to 10 times if we keep
1068 getting "EAGAIN" errors.
1073 static int failCt
= 0;
1076 fd
= chain_open (read_fd
,
1077 (tCC
**) p_fixd
->patch_args
,
1078 (process_chain_head
== -1)
1079 ? &process_chain_head
: (pid_t
*) NULL
);
1087 fprintf (stderr
, z_fork_err
, errno
, xstrerror (errno
),
1090 if ((errno
!= EAGAIN
) || (++failCt
> 10))
1091 exit (EXIT_FAILURE
);
1095 /* IF we allocated a shell script command,
1096 THEN free it and restore the command format to the fix description */
1097 if (pz_cmd
!= (char*)NULL
)
1099 free ((void*)pz_cmd
);
1100 p_fixd
->patch_args
[2] = pz_cmd_save
;
1108 /* * * * * * * * * * * * *
1110 Process the potential fixes for a particular include file.
1111 Input: the original text of the file and the file's name
1112 Result: none. A new file may or may not be created. */
1114 static t_bool fix_applies
PARAMS ((tFixDesc
*));
1116 fix_applies (p_fixd
)
1119 const char *pz_fname
= pz_curr_file
;
1120 const char *pz_scan
= p_fixd
->file_list
;
1124 # ifdef SEPARATE_FIX_PROC
1126 * There is only one fix that uses a shell script as of this writing.
1127 * I hope to nuke it anyway, it does not apply to DOS and it would
1128 * be painful to implement. Therefore, no "shell" fixes for DOS.
1130 if (p_fixd
->fd_flags
& (FD_SHELL_SCRIPT
| FD_SKIP_TEST
))
1133 if (p_fixd
->fd_flags
& FD_SKIP_TEST
)
1137 /* IF there is a file name restriction,
1138 THEN ensure the current file name matches one in the pattern */
1140 if (pz_scan
!= (char *) NULL
)
1144 while ((pz_fname
[0] == '.') && (pz_fname
[1] == '/'))
1146 name_len
= strlen (pz_fname
);
1150 pz_scan
= strstr (pz_scan
+ 1, pz_fname
);
1151 /* IF we can't match the string at all,
1153 if (pz_scan
== (char *) NULL
)
1156 /* IF the match is surrounded by the '|' markers,
1157 THEN we found a full match -- time to run the tests */
1159 if ((pz_scan
[-1] == '|') && (pz_scan
[name_len
] == '|'))
1164 /* FOR each test, see if it fails.
1165 IF it does fail, then we go on to the next test */
1167 for (p_test
= p_fixd
->p_test_desc
, test_ct
= p_fixd
->test_ct
;
1171 switch (p_test
->type
)
1174 if (test_test (p_test
, pz_curr_file
) != APPLY_FIX
) {
1176 if (VLEVEL( VERB_EVERYTHING
))
1177 fprintf (stderr
, z_failed
, "TEST", p_fixd
->fix_name
,
1178 pz_fname
, p_fixd
->test_ct
- test_ct
);
1185 if (egrep_test (pz_curr_data
, p_test
) != APPLY_FIX
) {
1187 if (VLEVEL( VERB_EVERYTHING
))
1188 fprintf (stderr
, z_failed
, "EGREP", p_fixd
->fix_name
,
1189 pz_fname
, p_fixd
->test_ct
- test_ct
);
1196 if (egrep_test (pz_curr_data
, p_test
) == APPLY_FIX
) {
1198 if (VLEVEL( VERB_EVERYTHING
))
1199 fprintf (stderr
, z_failed
, "NEGREP", p_fixd
->fix_name
,
1200 pz_fname
, p_fixd
->test_ct
- test_ct
);
1208 if (run_test (p_test
->pz_test_text
, pz_curr_file
, pz_curr_data
)
1211 if (VLEVEL( VERB_EVERYTHING
))
1212 fprintf (stderr
, z_failed
, "FTEST", p_fixd
->fix_name
,
1213 pz_fname
, p_fixd
->test_ct
- test_ct
);
1225 /* * * * * * * * * * * * *
1227 Write out a replacement file */
1229 static void write_replacement
PARAMS ((tFixDesc
*));
1231 write_replacement (p_fixd
)
1234 const char* pz_text
= p_fixd
->patch_args
[0];
1236 if ((pz_text
== (char*)NULL
) || (*pz_text
== NUL
))
1240 FILE* out_fp
= create_file ();
1241 fputs (pz_text
, out_fp
);
1247 /* * * * * * * * * * * * *
1249 We have work to do. Read back in the output
1250 of the filtering chain. Compare each byte as we read it with
1251 the contents of the original file. As soon as we find any
1252 difference, we will create the output file, write out all
1253 the matched text and then copy any remaining data from the
1254 output of the filter chain.
1256 static void test_for_changes
PARAMS ((int));
1258 test_for_changes (read_fd
)
1261 FILE *in_fp
= fdopen (read_fd
, "r");
1262 FILE *out_fp
= (FILE *) NULL
;
1263 unsigned char *pz_cmp
= (unsigned char*)pz_curr_data
;
1275 ch
&= 0xFF; /* all bytes are 8 bits */
1277 /* IF we are emitting the output
1278 THEN emit this character, too.
1280 if (out_fp
!= (FILE *) NULL
)
1283 /* ELSE if this character does not match the original,
1284 THEN now is the time to start the output.
1286 else if (ch
!= *pz_cmp
)
1288 out_fp
= create_file ();
1293 /* IF there are matched data, write the matched part now. */
1294 if ((char*)pz_cmp
!= pz_curr_data
)
1295 fwrite (pz_curr_data
, (size_t)((char*)pz_cmp
- pz_curr_data
),
1298 /* Emit the current unmatching character */
1302 /* ELSE the character matches. Advance the compare ptr */
1306 /* IF we created the output file, ... */
1307 if (out_fp
!= (FILE *) NULL
)
1311 /* Close the file and see if we have to worry about
1312 `#include "file.h"' constructs. */
1314 if (regexec (&incl_quote_re
, pz_curr_data
, 1, &match
, 0) == 0)
1315 extract_quoted_files (pz_curr_data
, pz_curr_file
, &match
);
1319 close (read_fd
); /* probably redundant, but I'm paranoid */
1323 /* * * * * * * * * * * * *
1325 Process the potential fixes for a particular include file.
1326 Input: the original text of the file and the file's name
1327 Result: none. A new file may or may not be created. */
1332 tFixDesc
*p_fixd
= fixDescList
;
1333 int todo_ct
= FIX_COUNT
;
1335 # ifndef SEPARATE_FIX_PROC
1336 int num_children
= 0;
1337 # else /* is SEPARATE_FIX_PROC */
1338 char* pz_file_source
= pz_curr_file
;
1341 if (access (pz_curr_file
, R_OK
) != 0)
1344 fprintf (stderr
, "Cannot access %s from %s\n\terror %d (%s)\n",
1345 pz_curr_file
, getcwd ((char *) NULL
, MAXPATHLEN
),
1346 erno
, xstrerror (erno
));
1350 pz_curr_data
= load_file (pz_curr_file
);
1351 if (pz_curr_data
== (char *) NULL
)
1357 if (VLEVEL( VERB_PROGRESS
) && have_tty
)
1358 fprintf (stderr
, "%6lu %-50s \r",
1359 (unsigned long) 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
);