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
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;
290 verbose_level
= VERB_PROGRESS
; break;
294 verbose_level
= VERB_TESTS
; break;
298 verbose_level
= VERB_EVERYTHING
; break;
300 if (verbose_level
>= VERB_EVERYTHING
) {
301 verbose_level
= VERB_EVERYTHING
;
302 fputs ("fixinc verbosity: EVERYTHING\n", stderr
);
304 while ((pz_find_base
[0] == '.') && (pz_find_base
[1] == '/'))
306 if ((pz_find_base
[0] != '.') || (pz_find_base
[1] != NUL
))
307 find_base_len
= strlen( pz_find_base
);
309 /* Compile all the regular expressions now.
310 That way, it is done only once for the whole run.
314 # ifdef SEPARATE_FIX_PROC
315 /* NULL as the first argument to `tempnam' causes it to DTRT
316 wrt the temporary directory where the file will be created. */
317 pz_temp_file
= tempnam( NULL
, "fxinc" );
320 signal (SIGQUIT
, SIG_IGN
);
322 signal (SIGIOT
, SIG_IGN
);
325 signal (SIGPIPE
, SIG_IGN
);
327 signal (SIGALRM
, SIG_IGN
);
328 signal (SIGTERM
, SIG_IGN
);
331 /* * * * * * * * * * * * *
333 load_file loads all the contents of a file into malloc-ed memory.
334 Its argument is the name of the file to read in; the returned
335 result is the NUL terminated contents of the file. The file
336 is presumed to be an ASCII text file containing no NULs. */
344 if (stat (fname
, &stbf
) != 0)
347 fprintf (stderr
, "error %d (%s) stat-ing %s\n",
348 errno
, xstrerror (errno
), fname
);
349 return (char *) NULL
;
351 if (stbf
.st_size
== 0)
354 /* Make the data map size one larger than the file size for documentation
355 purposes. Truth is that there will be a following NUL character if
356 the file size is not a multiple of the page size. If it is a multiple,
357 then this adjustment sometimes fails anyway. */
358 data_map_size
= stbf
.st_size
+1;
359 data_map_fd
= open (fname
, O_RDONLY
);
360 ttl_data_size
+= data_map_size
-1;
365 fprintf (stderr
, "error %d (%s) opening %s for read\n",
366 errno
, xstrerror (errno
), fname
);
370 #ifdef HAVE_MMAP_FILE
371 curr_data_mapped
= BOOL_TRUE
;
373 /* IF the file size is a multiple of the page size,
374 THEN sometimes you will seg fault trying to access a trailing byte */
375 if ((stbf
.st_size
& (getpagesize()-1)) == 0)
376 res
= (char*)BAD_ADDR
;
378 res
= (char*)mmap ((void*)NULL
, data_map_size
, PROT_READ
,
379 MAP_PRIVATE
, data_map_fd
, 0);
380 if (res
== (char*)BAD_ADDR
)
383 FILE* fp
= fdopen (data_map_fd
, "r");
384 curr_data_mapped
= BOOL_FALSE
;
385 res
= load_file_data (fp
);
392 static int machine_matches
PARAMS ((tFixDesc
*));
394 machine_matches( p_fixd
)
397 # ifndef SEPARATE_FIX_PROC
398 tSCC case_fmt
[] = "case %s in\n"; /* 9 bytes, plus string */
400 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
401 tSCC skip
[] = "skip"; /* 4 bytes */
402 tSCC run
[] = "run"; /* 3 bytes */
403 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
405 const char **papz_machs
= p_fixd
->papz_machs
;
407 const char *pz_sep
= "";
410 char cmd_buf
[ MACH_LIST_SIZE_LIMIT
]; /* size lim from fixincl.tpl */
412 /* Start the case statement */
414 sprintf (cmd_buf
, case_fmt
, pz_machine
);
415 pz
= cmd_buf
+ strlen (cmd_buf
);
417 /* Determine if a match means to apply the fix or not apply it */
419 if (p_fixd
->fd_flags
& FD_MACH_IFNOT
)
430 /* Emit all the machine names. If there are more than one,
431 then we will insert " | \\\n" between the names */
435 const char* pz_mach
= *(papz_machs
++);
437 if (pz_mach
== (const char*) NULL
)
439 sprintf (pz
, "%s%s", pz_sep
, pz_mach
);
444 /* Now emit the match and not-match actions and the esac */
446 sprintf (pz
, esac_fmt
, pz_if_true
, pz_if_false
);
449 The result will start either with 's' or 'r'. */
453 pz
= run_shell (cmd_buf
);
458 p_fixd
->fd_flags
|= FD_SKIP_TEST
;
464 # else /* is SEPARATE_FIX_PROC */
465 const char **papz_machs
= p_fixd
->papz_machs
;
466 int invert
= (p_fixd
->fd_flags
& FD_MACH_IFNOT
) != 0;
469 const char* pz_mach
= *(papz_machs
++);
471 if (pz_mach
== (const char*) NULL
)
473 if (strstr (pz_mach
, "dos") != NULL
&& !invert
)
477 p_fixd
->fd_flags
|= FD_SKIP_TEST
;
482 /* * * * * * * * * * * * *
484 run_compiles run all the regexp compiles for all the fixes once.
489 tFixDesc
*p_fixd
= fixDescList
;
490 int fix_ct
= FIX_COUNT
;
491 regex_t
*p_re
= (regex_t
*) xmalloc (REGEX_COUNT
* sizeof (regex_t
));
493 /* Make sure compile_re does not stumble across invalid data */
495 memset ( (void*)p_re
, '\0', REGEX_COUNT
* sizeof (regex_t
) );
496 memset ( (void*)&incl_quote_re
, '\0', sizeof (regex_t
) );
498 compile_re (incl_quote_pat
, &incl_quote_re
, 1,
499 "quoted include", "run_compiles");
501 /* Allow machine name tests to be ignored (testing, mainly) */
503 if (pz_machine
&& ((*pz_machine
== '\0') || (*pz_machine
== '*')))
504 pz_machine
= (char*)NULL
;
506 /* FOR every fixup, ... */
509 tTestDesc
*p_test
= p_fixd
->p_test_desc
;
510 int test_ct
= p_fixd
->test_ct
;
512 /* IF the machine type pointer is not NULL (we are not in test mode)
513 AND this test is for or not done on particular machines
516 if ( (pz_machine
!= NULL
)
517 && (p_fixd
->papz_machs
!= (const char**) NULL
)
518 && ! machine_matches (p_fixd
) )
521 /* FOR every test for the fixup, ... */
523 while (--test_ct
>= 0)
525 switch (p_test
->type
)
529 p_test
->p_test_regex
= p_re
++;
530 compile_re (p_test
->pz_test_text
, p_test
->p_test_regex
, 0,
531 "select test", p_fixd
->fix_name
);
537 while (p_fixd
++, --fix_ct
> 0);
541 /* * * * * * * * * * * * *
543 create_file Create the output modified file.
544 Input: the name of the file to create
545 Returns: a file pointer to the new, open file */
547 #if defined(S_IRUSR) && defined(S_IWUSR) && \
548 defined(S_IRGRP) && defined(S_IROTH)
550 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
552 # define S_IRALL 0644
555 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
556 defined(S_IROTH) && defined(S_IXOTH)
558 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
560 # define S_DIRALL 0755
564 static FILE *create_file
PARAMS ((void));
570 char fname
[MAXPATHLEN
];
572 sprintf (fname
, "%s/%s", pz_dest_dir
, pz_curr_file
+ find_base_len
);
574 fd
= open (fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRALL
);
576 /* We may need to create the directories needed... */
577 if ((fd
< 0) && (errno
== ENOENT
))
579 char *pz_dir
= strchr (fname
+ 1, '/');
582 while (pz_dir
!= (char *) NULL
)
585 if (stat (fname
, &stbf
) < 0)
587 mkdir (fname
, S_IFDIR
| S_DIRALL
);
591 pz_dir
= strchr (pz_dir
+ 1, '/');
594 /* Now, lets try the open again... */
595 fd
= open (fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRALL
);
599 fprintf (stderr
, "Error %d (%s) creating %s\n",
600 errno
, xstrerror (errno
), fname
);
604 fprintf (stderr
, "Fixed: %s\n", pz_curr_file
);
605 pf
= fdopen (fd
, "w");
608 * IF pz_machine is NULL, then we are in some sort of test mode.
609 * Do not insert the current directory name. Use a constant string.
611 fprintf (pf
, z_std_preamble
,
621 /* * * * * * * * * * * * *
623 test_test make sure a shell-style test expression passes.
624 Input: a pointer to the descriptor of the test to run and
625 the name of the file that we might want to fix
626 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
627 shell script we run. */
628 #ifndef SEPARATE_FIX_PROC
629 static int test_test
PARAMS ((tTestDesc
*, char *));
631 test_test (p_test
, pz_test_file
)
637 if ( test %s ) > /dev/null 2>&1\n\
645 static char cmd_buf
[4096];
647 sprintf (cmd_buf
, cmd_fmt
, pz_test_file
, p_test
->pz_test_text
);
648 pz_res
= run_shell (cmd_buf
);
660 fprintf (stderr
, "Script yielded bogus result of `%s':\n%s\n\n",
664 free ((void *) pz_res
);
669 * IF we are in MS-DOS land, then whatever shell-type test is required
670 * will, by definition, fail
672 #define test_test(t,tf) SKIP_FIX
675 /* * * * * * * * * * * * *
677 egrep_test make sure an egrep expression is found in the file text.
678 Input: a pointer to the descriptor of the test to run and
679 the pointer to the contents of the file under suspicion
680 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
682 The caller may choose to reverse meaning if the sense of the test
685 static int egrep_test
PARAMS ((char *, tTestDesc
*));
687 egrep_test (pz_data
, p_test
)
692 if (p_test
->p_test_regex
== 0)
693 fprintf (stderr
, "fixincl ERROR RE not compiled: `%s'\n",
694 p_test
->pz_test_text
);
696 if (regexec (p_test
->p_test_regex
, pz_data
, 0, 0, 0) == 0)
702 /* * * * * * * * * * * * *
704 quoted_file_exists Make sure that a file exists before we emit
705 the file name. If we emit the name, our invoking shell will try
706 to copy a non-existing file into the destination directory. */
708 static int quoted_file_exists
PARAMS ((const char *, const char *, const char *));
710 quoted_file_exists (pz_src_path
, pz_file_path
, pz_file
)
711 const char *pz_src_path
;
712 const char *pz_file_path
;
715 char z
[ MAXPATHLEN
];
717 sprintf (z
, "%s/%s/", pz_src_path
, pz_file_path
);
718 pz
= z
+ strlen ( z
);
721 char ch
= *pz_file
++;
731 if (stat (z
, &s
) != 0)
733 return S_ISREG( s
.st_mode
);
738 /* * * * * * * * * * * * *
742 The syntax, `#include "file.h"' specifies that the compiler is to
743 search the local directory of the current file before the include
744 list. Consequently, if we have modified a header and stored it in
745 another directory, any files that are included by that modified
746 file in that fashion must also be copied into this new directory.
747 This routine finds those flavors of #include and for each one found
750 1. source directory of the original file
751 2. the relative path file name of the #includ-ed file
752 3. the full destination path for this file
754 Input: the text of the file, the file name and a pointer to the
755 match list where the match information was stored.
756 Result: internally nothing. The results are written to stdout
757 for interpretation by the invoking shell */
760 static void extract_quoted_files
PARAMS ((char *, const char *, regmatch_t
*));
762 extract_quoted_files (pz_data
, pz_fixed_file
, p_re_match
)
764 const char *pz_fixed_file
;
765 regmatch_t
*p_re_match
;
767 char *pz_dir_end
= strrchr (pz_fixed_file
, '/');
768 char *pz_incl_quot
= pz_data
;
770 if (VLEVEL( VERB_APPLIES
))
771 fprintf (stderr
, "Quoted includes in %s\n", pz_fixed_file
);
773 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
774 If there is none, then it is in our current directory, ".". */
776 if (pz_dir_end
== (char *) NULL
)
783 pz_incl_quot
+= p_re_match
->rm_so
;
785 /* Skip forward to the included file name */
786 while (*pz_incl_quot
!= '"')
789 if (quoted_file_exists (pz_src_dir
, pz_fixed_file
, pz_incl_quot
))
791 /* Print the source directory and the subdirectory
792 of the file in question. */
793 printf ("%s %s/", pz_src_dir
, pz_fixed_file
);
794 pz_dir_end
= pz_incl_quot
;
796 /* Append to the directory the relative path of the desired file */
797 while (*pz_incl_quot
!= '"')
798 putc (*pz_incl_quot
++, stdout
);
800 /* Now print the destination directory appended with the
801 relative path of the desired file */
802 printf (" %s/%s/", pz_dest_dir
, pz_fixed_file
);
803 while (*pz_dir_end
!= '"')
804 putc (*pz_dir_end
++, stdout
);
810 /* Find the next entry */
811 if (regexec (&incl_quote_re
, pz_incl_quot
, 1, p_re_match
, 0) != 0)
817 /* * * * * * * * * * * * *
819 Somebody wrote a *_fix subroutine that we must call.
821 #ifndef SEPARATE_FIX_PROC
822 static int internal_fix
PARAMS ((int, tFixDesc
*));
824 internal_fix (read_fd
, p_fixd
)
832 fprintf (stderr
, "Error %d on pipe(2) call\n", errno
);
838 pid_t childid
= fork();
861 fprintf (stderr
, z_fork_err
, errno
, xstrerror (errno
),
864 static int failCt
= 0;
865 if ((errno
!= EAGAIN
) || (++failCt
> 10))
872 * Close our current stdin and stdout
874 close (STDIN_FILENO
);
875 close (STDOUT_FILENO
);
879 * Make the fd passed in the stdin, and the write end of
880 * the new pipe become the stdout.
882 fcntl (fd
[1], F_DUPFD
, STDOUT_FILENO
);
883 fcntl (read_fd
, F_DUPFD
, STDIN_FILENO
);
885 apply_fix (p_fixd
, pz_curr_file
);
888 #endif /* !SEPARATE_FIX_PROC */
891 #ifdef SEPARATE_FIX_PROC
893 fix_with_system (p_fixd
, pz_fix_file
, pz_file_source
, pz_temp_file
)
903 if (p_fixd
->fd_flags
& FD_SUBROUTINE
)
905 tSCC z_applyfix_prog
[] = "/fixinc/applyfix";
908 + strlen( pz_orig_dir
)
909 + sizeof( z_applyfix_prog
)
910 + strlen( pz_fix_file
)
911 + strlen( pz_file_source
)
912 + strlen( pz_temp_file
);
914 pz_cmd
= (char*)xmalloc( argsize
);
916 strcpy( pz_cmd
, pz_orig_dir
);
917 pz_scan
= pz_cmd
+ strlen( pz_orig_dir
);
918 strcpy( pz_scan
, z_applyfix_prog
);
919 pz_scan
+= sizeof( z_applyfix_prog
) - 1;
923 * Now add the fix number and file names that may be needed
925 sprintf (pz_scan
, "%ld \'%s\' \'%s\' \'%s\'", p_fixd
- fixDescList
,
926 pz_fix_file
, pz_file_source
, pz_temp_file
);
928 else /* NOT an "internal" fix: */
932 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
933 dst is a temporary file anyway, so we know there's no other
934 file by that name; and DOS's system(3) doesn't mind to
935 clobber existing file in redirection. Besides, with DOS 8+3
936 limited file namespace, we can easily lose if dst already has
937 an extension that is 3 or more characters long.
939 I do not think the 8+3 issue is relevant because all the files
940 we operate on are named "*.h", making 8+2 adequate. Anyway,
941 the following bizarre use of 'cat' only works on DOS boxes.
942 It causes the file to be dropped into a temporary file for
943 'cat' to read (pipes do not work on DOS). */
944 tSCC z_cmd_fmt
[] = " \'%s\' | cat > \'%s\'";
946 /* Don't use positional formatting arguments because some lame-o
947 implementations cannot cope :-(. */
948 tSCC z_cmd_fmt
[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
950 tCC
** ppArgs
= p_fixd
->patch_args
;
952 argsize
= sizeof( z_cmd_fmt
) + strlen( pz_temp_file
)
953 + strlen( pz_file_source
);
958 * Compute the size of the command line. Add lotsa extra space
959 * because some of the args to sed use lotsa single quotes.
960 * (This requires three extra bytes per quote. Here we allow
961 * for up to 8 single quotes for each argument, including the
962 * command name "sed" itself. Nobody will *ever* need more. :)
966 tCC
* p_arg
= *(ppArgs
++);
969 argsize
+= 24 + strlen( p_arg
);
972 /* Estimated buffer size we will need. */
973 pz_scan
= pz_cmd
= (char*)xmalloc( argsize
);
974 /* How much of it do we allot to the program name and its
976 parg_size
= argsize
- parg_size
;
978 ppArgs
= p_fixd
->patch_args
;
981 * Copy the program name, unquoted
984 tCC
* pArg
= *(ppArgs
++);
995 * Copy the program arguments, quoted
999 tCC
* pArg
= *(ppArgs
++);
1004 pz_scan
= make_raw_shell_str( pz_scan_save
= pz_scan
, pArg
,
1005 parg_size
- (pz_scan
- pz_cmd
) );
1007 * Make sure we don't overflow the buffer due to sloppy
1010 while (pz_scan
== (char*)NULL
)
1012 size_t already_filled
= pz_scan_save
- pz_cmd
;
1013 pz_cmd
= (char*)xrealloc( pz_cmd
, argsize
+= 100 );
1014 pz_scan_save
= pz_scan
= pz_cmd
+ already_filled
;
1016 pz_scan
= make_raw_shell_str( pz_scan
, pArg
,
1017 parg_size
- (pz_scan
- pz_cmd
) );
1022 * add the file machinations.
1025 sprintf (pz_scan
, z_cmd_fmt
, pz_file_source
, pz_temp_file
);
1027 sprintf (pz_scan
, z_cmd_fmt
, pz_file_source
, pz_temp_file
,
1028 pz_temp_file
, pz_temp_file
, pz_temp_file
);
1032 free( (void*)pz_cmd
);
1035 /* * * * * * * * * * * * *
1037 This loop should only cycle for 1/2 of one loop.
1038 "chain_open" starts a process that uses "read_fd" as
1039 its stdin and returns the new fd this process will use
1042 #else /* is *NOT* SEPARATE_FIX_PROC */
1043 static int start_fixer
PARAMS ((int, tFixDesc
*, char *));
1045 start_fixer (read_fd
, p_fixd
, pz_fix_file
)
1053 if ((p_fixd
->fd_flags
& FD_SUBROUTINE
) != 0)
1054 return internal_fix (read_fd
, p_fixd
);
1056 if ((p_fixd
->fd_flags
& FD_SHELL_SCRIPT
) == 0)
1057 pz_cmd
= (char*)NULL
;
1060 tSCC z_cmd_fmt
[] = "file='%s'\n%s";
1061 pz_cmd
= (char*) xmalloc (strlen (p_fixd
->patch_args
[2])
1062 + sizeof( z_cmd_fmt
)
1063 + strlen( pz_fix_file
));
1064 sprintf (pz_cmd
, z_cmd_fmt
, pz_fix_file
, p_fixd
->patch_args
[2]);
1065 pz_cmd_save
= p_fixd
->patch_args
[2];
1066 p_fixd
->patch_args
[2] = pz_cmd
;
1069 /* Start a fix process, handing off the previous read fd for its
1070 stdin and getting a new fd that reads from the fix process' stdout.
1071 We normally will not loop, but we will up to 10 times if we keep
1072 getting "EAGAIN" errors.
1077 static int failCt
= 0;
1080 fd
= chain_open (read_fd
,
1081 (tCC
**) p_fixd
->patch_args
,
1082 (process_chain_head
== -1)
1083 ? &process_chain_head
: (pid_t
*) NULL
);
1091 fprintf (stderr
, z_fork_err
, errno
, xstrerror (errno
),
1094 if ((errno
!= EAGAIN
) || (++failCt
> 10))
1095 exit (EXIT_FAILURE
);
1099 /* IF we allocated a shell script command,
1100 THEN free it and restore the command format to the fix description */
1101 if (pz_cmd
!= (char*)NULL
)
1103 free ((void*)pz_cmd
);
1104 p_fixd
->patch_args
[2] = pz_cmd_save
;
1112 /* * * * * * * * * * * * *
1114 Process the potential fixes for a particular include file.
1115 Input: the original text of the file and the file's name
1116 Result: none. A new file may or may not be created. */
1118 static t_bool fix_applies
PARAMS ((tFixDesc
*));
1120 fix_applies (p_fixd
)
1123 const char *pz_fname
= pz_curr_file
;
1124 const char *pz_scan
= p_fixd
->file_list
;
1128 # ifdef SEPARATE_FIX_PROC
1130 * There is only one fix that uses a shell script as of this writing.
1131 * I hope to nuke it anyway, it does not apply to DOS and it would
1132 * be painful to implement. Therefore, no "shell" fixes for DOS.
1134 if (p_fixd
->fd_flags
& (FD_SHELL_SCRIPT
| FD_SKIP_TEST
))
1137 if (p_fixd
->fd_flags
& FD_SKIP_TEST
)
1141 /* IF there is a file name restriction,
1142 THEN ensure the current file name matches one in the pattern */
1144 if (pz_scan
!= (char *) NULL
)
1148 while ((pz_fname
[0] == '.') && (pz_fname
[1] == '/'))
1150 name_len
= strlen (pz_fname
);
1154 pz_scan
= strstr (pz_scan
+ 1, pz_fname
);
1155 /* IF we can't match the string at all,
1157 if (pz_scan
== (char *) NULL
)
1160 /* IF the match is surrounded by the '|' markers,
1161 THEN we found a full match -- time to run the tests */
1163 if ((pz_scan
[-1] == '|') && (pz_scan
[name_len
] == '|'))
1168 /* FOR each test, see if it fails.
1169 IF it does fail, then we go on to the next test */
1171 for (p_test
= p_fixd
->p_test_desc
, test_ct
= p_fixd
->test_ct
;
1175 switch (p_test
->type
)
1178 if (test_test (p_test
, pz_curr_file
) != APPLY_FIX
) {
1180 if (VLEVEL( VERB_EVERYTHING
))
1181 fprintf (stderr
, z_failed
, "TEST", p_fixd
->fix_name
,
1182 pz_fname
, p_fixd
->test_ct
- test_ct
);
1189 if (egrep_test (pz_curr_data
, p_test
) != APPLY_FIX
) {
1191 if (VLEVEL( VERB_EVERYTHING
))
1192 fprintf (stderr
, z_failed
, "EGREP", p_fixd
->fix_name
,
1193 pz_fname
, p_fixd
->test_ct
- test_ct
);
1200 if (egrep_test (pz_curr_data
, p_test
) == APPLY_FIX
) {
1202 if (VLEVEL( VERB_EVERYTHING
))
1203 fprintf (stderr
, z_failed
, "NEGREP", p_fixd
->fix_name
,
1204 pz_fname
, p_fixd
->test_ct
- test_ct
);
1212 if (run_test (p_test
->pz_test_text
, pz_curr_file
, pz_curr_data
)
1215 if (VLEVEL( VERB_EVERYTHING
))
1216 fprintf (stderr
, z_failed
, "FTEST", p_fixd
->fix_name
,
1217 pz_fname
, p_fixd
->test_ct
- test_ct
);
1229 /* * * * * * * * * * * * *
1231 Write out a replacement file */
1233 static void write_replacement
PARAMS ((tFixDesc
*));
1235 write_replacement (p_fixd
)
1238 const char* pz_text
= p_fixd
->patch_args
[0];
1240 if ((pz_text
== (char*)NULL
) || (*pz_text
== NUL
))
1244 FILE* out_fp
= create_file ();
1245 fputs (pz_text
, out_fp
);
1251 /* * * * * * * * * * * * *
1253 We have work to do. Read back in the output
1254 of the filtering chain. Compare each byte as we read it with
1255 the contents of the original file. As soon as we find any
1256 difference, we will create the output file, write out all
1257 the matched text and then copy any remaining data from the
1258 output of the filter chain.
1260 static void test_for_changes
PARAMS ((int));
1262 test_for_changes (read_fd
)
1265 FILE *in_fp
= fdopen (read_fd
, "r");
1266 FILE *out_fp
= (FILE *) NULL
;
1267 unsigned char *pz_cmp
= (unsigned char*)pz_curr_data
;
1279 ch
&= 0xFF; /* all bytes are 8 bits */
1281 /* IF we are emitting the output
1282 THEN emit this character, too.
1284 if (out_fp
!= (FILE *) NULL
)
1287 /* ELSE if this character does not match the original,
1288 THEN now is the time to start the output.
1290 else if (ch
!= *pz_cmp
)
1292 out_fp
= create_file ();
1297 /* IF there are matched data, write the matched part now. */
1298 if ((char*)pz_cmp
!= pz_curr_data
)
1299 fwrite (pz_curr_data
, (size_t)((char*)pz_cmp
- pz_curr_data
),
1302 /* Emit the current unmatching character */
1306 /* ELSE the character matches. Advance the compare ptr */
1310 /* IF we created the output file, ... */
1311 if (out_fp
!= (FILE *) NULL
)
1315 /* Close the file and see if we have to worry about
1316 `#include "file.h"' constructs. */
1318 if (regexec (&incl_quote_re
, pz_curr_data
, 1, &match
, 0) == 0)
1319 extract_quoted_files (pz_curr_data
, pz_curr_file
, &match
);
1323 close (read_fd
); /* probably redundant, but I'm paranoid */
1327 /* * * * * * * * * * * * *
1329 Process the potential fixes for a particular include file.
1330 Input: the original text of the file and the file's name
1331 Result: none. A new file may or may not be created. */
1336 tFixDesc
*p_fixd
= fixDescList
;
1337 int todo_ct
= FIX_COUNT
;
1339 # ifndef SEPARATE_FIX_PROC
1340 int num_children
= 0;
1341 # else /* is SEPARATE_FIX_PROC */
1342 char* pz_file_source
= pz_curr_file
;
1345 if (access (pz_curr_file
, R_OK
) != 0)
1348 fprintf (stderr
, "Cannot access %s from %s\n\terror %d (%s)\n",
1349 pz_curr_file
, getcwd ((char *) NULL
, MAXPATHLEN
),
1350 erno
, xstrerror (erno
));
1354 pz_curr_data
= load_file (pz_curr_file
);
1355 if (pz_curr_data
== (char *) NULL
)
1361 if (VLEVEL( VERB_PROGRESS
) && have_tty
)
1362 fprintf (stderr
, "%6lu %-50s \r",
1363 (unsigned long) data_map_size
, pz_curr_file
);
1365 # ifndef SEPARATE_FIX_PROC
1366 process_chain_head
= NOPROCESS
;
1368 /* For every fix in our fix list, ... */
1369 for (; todo_ct
> 0; p_fixd
++, todo_ct
--)
1371 if (! fix_applies (p_fixd
))
1374 if (VLEVEL( VERB_APPLIES
))
1375 fprintf (stderr
, "Applying %-24s to %s\n",
1376 p_fixd
->fix_name
, pz_curr_file
);
1378 if (p_fixd
->fd_flags
& FD_REPLACEMENT
)
1380 write_replacement (p_fixd
);
1385 /* IF we do not have a read pointer,
1386 THEN this is the first fix for the current file.
1387 Open the source file. That will be used as stdin for
1388 the first fix. Any subsequent fixes will use the
1389 stdout descriptor of the previous fix for its stdin. */
1393 read_fd
= open (pz_curr_file
, O_RDONLY
);
1396 fprintf (stderr
, "Error %d (%s) opening %s\n", errno
,
1397 xstrerror (errno
), pz_curr_file
);
1398 exit (EXIT_FAILURE
);
1401 /* Ensure we do not get duplicate output */
1406 read_fd
= start_fixer (read_fd
, p_fixd
, pz_curr_file
);
1410 /* IF we have a read-back file descriptor,
1411 THEN check for changes and write output if changed. */
1415 test_for_changes (read_fd
);
1417 apply_ct
+= num_children
;
1419 /* Wait for child processes created by chain_open()
1420 to avoid leaving zombies. */
1422 wait ((int *) NULL
);
1423 } while (--num_children
> 0);
1426 # else /* is SEPARATE_FIX_PROC */
1428 for (; todo_ct
> 0; p_fixd
++, todo_ct
--)
1430 if (! fix_applies (p_fixd
))
1433 if (VLEVEL( VERB_APPLIES
))
1434 fprintf (stderr
, "Applying %-24s to %s\n",
1435 p_fixd
->fix_name
, pz_curr_file
);
1437 if (p_fixd
->fd_flags
& FD_REPLACEMENT
)
1439 write_replacement (p_fixd
);
1443 fix_with_system (p_fixd
, pz_curr_file
, pz_file_source
, pz_temp_file
);
1444 pz_file_source
= pz_temp_file
;
1447 read_fd
= open (pz_temp_file
, O_RDONLY
);
1450 if (errno
!= ENOENT
)
1451 fprintf (stderr
, "error %d (%s) opening output (%s) for read\n",
1452 errno
, xstrerror (errno
), pz_temp_file
);
1456 test_for_changes (read_fd
);
1457 /* Unlinking a file while it is still open is a Bad Idea on
1460 unlink (pz_temp_file
);