2003-03-15 Glen Nakamura <glen@imodulo.com>
[official-gcc.git] / gcc / fixinc / fixincl.c
blob7c38637b1d0a682309c870e891a5db1915c97723
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)
12 any later version.
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. */
24 #include "fixlib.h"
26 #if defined( HAVE_MMAP_FILE )
27 #include <sys/mman.h>
28 #define BAD_ADDR ((void*)-1)
29 #endif
31 #if ! defined( SIGCHLD ) && defined( SIGCLD )
32 # define SIGCHLD SIGCLD
33 #endif
34 #ifndef SEPARATE_FIX_PROC
35 #include "server.h"
36 #endif
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\
48 \t\"%s/%s\"\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;
55 ENV_TABLE
56 #undef _ENV_
58 int find_base_len = 0;
60 typedef enum {
61 VERB_SILENT = 0,
62 VERB_FIXES,
63 VERB_APPLIES,
64 VERB_PROGRESS,
65 VERB_TESTS,
66 VERB_EVERYTHING
67 } te_verbose;
69 te_verbose verbose_level = VERB_PROGRESS;
70 int have_tty = 0;
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;
82 int data_map_fd;
83 size_t data_map_size;
84 size_t ttl_data_size = 0;
86 #ifdef DO_STATS
87 int process_ct = 0;
88 int apply_ct = 0;
89 int fixed_ct = 0;
90 int altered_ct = 0;
91 #endif /* DO_STATS */
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 */
105 #include "fixincl.x"
107 /* * * * * * * * * * * * * * * * * * *
109 * MAIN ROUTINE
111 extern int main PARAMS ((int, char **));
113 main (argc, argv)
114 int argc;
115 char **argv;
117 char *file_name_buf;
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
128 by accident */
130 freopen ("/dev/null", "r", stdin);
132 if (file_name_buf == (char *) NULL)
134 fputs ("No file names listed for fixing\n", stderr);
135 exit (EXIT_FAILURE);
138 for (;;)
140 char* pz_end;
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] == '/'))
146 file_name_buf += 2;
148 /* Check for end of list */
150 if (*file_name_buf == NUL)
151 break;
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);
159 else
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 == '#'))
167 continue;
168 *pz_end = NUL;
170 process ();
171 } /* for (;;) */
173 #ifdef DO_STATS
174 if (VLEVEL( VERB_PROGRESS )) {
175 tSCC zFmt[] =
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 );
188 # endif
189 exit (EXIT_SUCCESS);
193 static void
194 do_version ()
196 static const char zFmt[] = "echo '%s'";
197 char zBuf[ 1024 ];
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
204 run_compiles ();
205 sprintf (zBuf, zFmt, program_id);
206 #ifndef SEPARATE_FIX_PROC
207 puts (zBuf + 5);
208 exit (strcmp (run_shell (zBuf), program_id));
209 #else
210 exit (system (zBuf));
211 #endif
214 /* * * * * * * * * * * * */
216 void
217 initialize ( argc, argv )
218 int argc;
219 char** argv;
221 static const char var_not_found[] =
222 #ifndef __STDC__
223 "fixincl ERROR: %s environment variable not defined\n"
224 #else
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"
228 ENV_TABLE
229 # undef _ENV_
230 #endif
233 xmalloc_set_program_name (argv[0]);
235 switch (argc)
237 case 1:
238 break;
240 case 2:
241 if (strcmp (argv[1], "-v") == 0)
242 do_version ();
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] );
247 exit (EXIT_FAILURE);
249 break;
251 default:
252 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
253 exit (EXIT_FAILURE);
256 #ifdef SIGCHLD
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);
260 #endif
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); } }
267 ENV_TABLE
269 #undef _ENV_
271 if (ISDIGIT ( *pz_verbose ))
272 verbose_level = (te_verbose)atoi( pz_verbose );
273 else
274 switch (*pz_verbose) {
275 case 's':
276 case 'S':
277 verbose_level = VERB_SILENT; break;
279 case 'f':
280 case 'F':
281 verbose_level = VERB_FIXES; break;
283 case 'a':
284 case 'A':
285 verbose_level = VERB_APPLIES; break;
287 case 'p':
288 case 'P':
289 verbose_level = VERB_PROGRESS; break;
291 case 't':
292 case 'T':
293 verbose_level = VERB_TESTS; break;
295 case 'e':
296 case 'E':
297 verbose_level = VERB_EVERYTHING; break;
300 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
301 pz_find_base += 2;
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.
308 run_compiles ();
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" );
314 # endif
316 signal (SIGQUIT, SIG_IGN);
317 #ifdef SIGIOT
318 signal (SIGIOT, SIG_IGN);
319 #endif
320 #ifdef SIGPIPE
321 signal (SIGPIPE, SIG_IGN);
322 #endif
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. */
333 char *
334 load_file ( fname )
335 const char* fname;
337 struct stat stbf;
338 char* res;
340 if (stat (fname, &stbf) != 0)
342 if (NOT_SILENT)
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)
348 return (char*)NULL;
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;
358 if (data_map_fd < 0)
360 if (NOT_SILENT)
361 fprintf (stderr, "error %d (%s) opening %s for read\n",
362 errno, xstrerror (errno), fname);
363 return (char*)NULL;
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;
373 else
374 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
375 MAP_PRIVATE, data_map_fd, 0);
376 if (res == (char*)BAD_ADDR)
377 #endif
379 FILE* fp = fdopen (data_map_fd, "r");
380 curr_data_mapped = BOOL_FALSE;
381 res = load_file_data (fp);
382 fclose (fp);
385 return res;
388 static int machine_matches PARAMS ((tFixDesc *));
389 static int
390 machine_matches( p_fixd )
391 tFixDesc *p_fixd;
393 # ifndef SEPARATE_FIX_PROC
394 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
395 tSCC esac_fmt[] =
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;
402 char *pz;
403 const char *pz_sep = "";
404 tCC *pz_if_true;
405 tCC *pz_if_false;
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)
417 pz_if_true = skip;
418 pz_if_false = run;
420 else
422 pz_if_true = run;
423 pz_if_false = skip;
426 /* Emit all the machine names. If there are more than one,
427 then we will insert " | \\\n" between the names */
429 for (;;)
431 const char* pz_mach = *(papz_machs++);
433 if (pz_mach == (const char*) NULL)
434 break;
435 sprintf (pz, "%s%s", pz_sep, pz_mach);
436 pz += strlen (pz);
437 pz_sep = " | \\\n";
440 /* Now emit the match and not-match actions and the esac */
442 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
444 /* Run the script.
445 The result will start either with 's' or 'r'. */
448 int skip;
449 pz = run_shell (cmd_buf);
450 skip = (*pz == 's');
451 free ( (void*)pz );
452 if (skip)
454 p_fixd->fd_flags |= FD_SKIP_TEST;
455 return BOOL_FALSE;
459 return BOOL_TRUE;
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;
463 for (;;)
465 const char* pz_mach = *(papz_machs++);
467 if (pz_mach == (const char*) NULL)
468 break;
469 if (strstr (pz_mach, "dos") != NULL && !invert)
470 return BOOL_TRUE;
473 p_fixd->fd_flags |= FD_SKIP_TEST;
474 return BOOL_FALSE;
475 # endif
478 /* * * * * * * * * * * * *
480 run_compiles run all the regexp compiles for all the fixes once.
482 void
483 run_compiles ()
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
510 THEN ... */
512 if ( (pz_machine != NULL)
513 && (p_fixd->papz_machs != (const char**) NULL)
514 && ! machine_matches (p_fixd) )
515 continue;
517 /* FOR every test for the fixup, ... */
519 while (--test_ct >= 0)
521 switch (p_test->type)
523 case TT_EGREP:
524 case TT_NEGREP:
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);
528 default: break;
530 p_test++;
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)
547 #else
548 # define S_IRALL 0644
549 #endif
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)
555 #else
556 # define S_DIRALL 0755
557 #endif
560 static FILE *create_file PARAMS ((void));
561 static FILE *
562 create_file ()
564 int fd;
565 FILE *pf;
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, '/');
576 struct stat stbf;
578 while (pz_dir != (char *) NULL)
580 *pz_dir = NUL;
581 if (stat (fname, &stbf) < 0)
583 mkdir (fname, S_IFDIR | S_DIRALL);
586 *pz_dir = '/';
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);
593 if (fd < 0)
595 fprintf (stderr, "Error %d (%s) creating %s\n",
596 errno, xstrerror (errno), fname);
597 exit (EXIT_FAILURE);
599 if (NOT_SILENT)
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,
608 (pz_machine == NULL)
609 ? "fixinc/tests/inc"
610 : pz_input_dir,
611 pz_curr_file);
613 return pf;
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 *));
626 static int
627 test_test (p_test, pz_test_file)
628 tTestDesc *p_test;
629 char* pz_test_file;
631 tSCC cmd_fmt[] =
632 "file=%s\n\
633 if ( test %s ) > /dev/null 2>&1\n\
634 then echo TRUE\n\
635 else echo FALSE\n\
636 fi";
638 char *pz_res;
639 int res;
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);
646 switch (*pz_res) {
647 case 'T':
648 res = APPLY_FIX;
649 break;
651 case 'F':
652 res = SKIP_FIX;
653 break;
655 default:
656 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
657 pz_res, cmd_buf );
660 free ((void *) pz_res);
661 return res;
663 #else
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
669 #endif
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
679 is inverted. */
681 static int egrep_test PARAMS ((char *, tTestDesc *));
682 static int
683 egrep_test (pz_data, p_test)
684 char *pz_data;
685 tTestDesc *p_test;
687 #ifdef DEBUG
688 if (p_test->p_test_regex == 0)
689 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
690 p_test->pz_test_text);
691 #endif
692 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
693 return APPLY_FIX;
694 return SKIP_FIX;
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 *));
705 static int
706 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
707 const char *pz_src_path;
708 const char *pz_file_path;
709 const char *pz_file;
711 char z[ MAXPATHLEN ];
712 char* pz;
713 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
714 pz = z + strlen ( z );
716 for (;;) {
717 char ch = *pz_file++;
718 if (! ISGRAPH( ch ))
719 return 0;
720 if (ch == '"')
721 break;
722 *pz++ = ch;
724 *pz = '\0';
726 struct stat s;
727 if (stat (z, &s) != 0)
728 return 0;
729 return S_ISREG( s.st_mode );
734 /* * * * * * * * * * * * *
736 extract_quoted_files
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
744 emits a triple of:
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 *));
757 static void
758 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
759 char *pz_data;
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)
773 pz_fixed_file = ".";
774 else
775 *pz_dir_end = '\0';
777 for (;;)
779 pz_incl_quot += p_re_match->rm_so;
781 /* Skip forward to the included file name */
782 while (*pz_incl_quot != '"')
783 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);
802 /* End of entry */
803 putc ('\n', stdout);
806 /* Find the next entry */
807 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
808 break;
813 /* * * * * * * * * * * * *
815 Somebody wrote a *_fix subroutine that we must call.
817 #ifndef SEPARATE_FIX_PROC
818 static int internal_fix PARAMS ((int, tFixDesc *));
819 static int
820 internal_fix (read_fd, p_fixd)
821 int read_fd;
822 tFixDesc* p_fixd;
824 int fd[2];
826 if (pipe( fd ) != 0)
828 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
829 exit (EXIT_FAILURE);
832 for (;;)
834 pid_t childid = fork();
836 switch (childid)
838 case -1:
839 break;
841 case 0:
842 close (fd[0]);
843 goto do_child_task;
845 default:
847 * Parent process
849 close (read_fd);
850 close (fd[1]);
851 return fd[0];
855 * Parent in error
857 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
858 p_fixd->fix_name);
860 static int failCt = 0;
861 if ((errno != EAGAIN) || (++failCt > 10))
862 exit (EXIT_FAILURE);
863 sleep (1);
865 } do_child_task:;
868 * Close our current stdin and stdout
870 close (STDIN_FILENO);
871 close (STDOUT_FILENO);
872 UNLOAD_DATA();
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);
882 exit (0);
884 #endif /* !SEPARATE_FIX_PROC */
887 #ifdef SEPARATE_FIX_PROC
888 static void
889 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
890 tFixDesc* p_fixd;
891 tCC* pz_fix_file;
892 tCC* pz_file_source;
893 tCC* pz_temp_file;
895 char* pz_cmd;
896 char* pz_scan;
897 size_t argsize;
899 if (p_fixd->fd_flags & FD_SUBROUTINE)
901 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
903 argsize = 32
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;
916 *(pz_scan++) = ' ';
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: */
926 size_t parg_size;
927 #ifdef __MSDOS__
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\'";
941 #else
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";
945 #endif
946 tCC** ppArgs = p_fixd->patch_args;
948 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
949 + strlen( pz_file_source );
950 parg_size = argsize;
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. :)
960 for (;;)
962 tCC* p_arg = *(ppArgs++);
963 if (p_arg == NULL)
964 break;
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
971 arguments. */
972 parg_size = argsize - parg_size;
974 ppArgs = p_fixd->patch_args;
977 * Copy the program name, unquoted
980 tCC* pArg = *(ppArgs++);
981 for (;;)
983 char ch = *(pArg++);
984 if (ch == NUL)
985 break;
986 *(pz_scan++) = ch;
991 * Copy the program arguments, quoted
993 for (;;)
995 tCC* pArg = *(ppArgs++);
996 char* pz_scan_save;
997 if (pArg == NULL)
998 break;
999 *(pz_scan++) = ' ';
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
1004 * size estimation.
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;
1011 parg_size += 100;
1012 pz_scan = make_raw_shell_str( pz_scan, pArg,
1013 parg_size - (pz_scan - pz_cmd) );
1018 * add the file machinations.
1020 #ifdef __MSDOS__
1021 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1022 #else
1023 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1024 pz_temp_file, pz_temp_file, pz_temp_file);
1025 #endif
1027 system( pz_cmd );
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
1036 for stdout. */
1038 #else /* is *NOT* SEPARATE_FIX_PROC */
1039 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1040 static int
1041 start_fixer (read_fd, p_fixd, pz_fix_file)
1042 int read_fd;
1043 tFixDesc* p_fixd;
1044 char* pz_fix_file;
1046 tCC* pz_cmd_save;
1047 char* pz_cmd;
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;
1054 else
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.
1071 for (;;)
1073 static int failCt = 0;
1074 int fd;
1076 fd = chain_open (read_fd,
1077 (tCC **) p_fixd->patch_args,
1078 (process_chain_head == -1)
1079 ? &process_chain_head : (pid_t *) NULL);
1081 if (fd != -1)
1083 read_fd = fd;
1084 break;
1087 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1088 p_fixd->fix_name);
1090 if ((errno != EAGAIN) || (++failCt > 10))
1091 exit (EXIT_FAILURE);
1092 sleep (1);
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;
1103 return read_fd;
1105 #endif
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 *));
1115 static t_bool
1116 fix_applies (p_fixd)
1117 tFixDesc *p_fixd;
1119 const char *pz_fname = pz_curr_file;
1120 const char *pz_scan = p_fixd->file_list;
1121 int test_ct;
1122 tTestDesc *p_test;
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))
1131 return BOOL_FALSE;
1132 # else
1133 if (p_fixd->fd_flags & FD_SKIP_TEST)
1134 return BOOL_FALSE;
1135 # endif
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)
1142 size_t name_len;
1144 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1145 pz_fname += 2;
1146 name_len = strlen (pz_fname);
1148 for (;;)
1150 pz_scan = strstr (pz_scan + 1, pz_fname);
1151 /* IF we can't match the string at all,
1152 THEN bail */
1153 if (pz_scan == (char *) NULL)
1154 return BOOL_FALSE;
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] == '|'))
1160 break;
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;
1168 test_ct-- > 0;
1169 p_test++)
1171 switch (p_test->type)
1173 case TT_TEST:
1174 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1175 #ifdef DEBUG
1176 if (VLEVEL( VERB_EVERYTHING ))
1177 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1178 pz_fname, p_fixd->test_ct - test_ct);
1179 #endif
1180 return BOOL_FALSE;
1182 break;
1184 case TT_EGREP:
1185 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1186 #ifdef DEBUG
1187 if (VLEVEL( VERB_EVERYTHING ))
1188 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1189 pz_fname, p_fixd->test_ct - test_ct);
1190 #endif
1191 return BOOL_FALSE;
1193 break;
1195 case TT_NEGREP:
1196 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1197 #ifdef DEBUG
1198 if (VLEVEL( VERB_EVERYTHING ))
1199 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1200 pz_fname, p_fixd->test_ct - test_ct);
1201 #endif
1202 /* Negated sense */
1203 return BOOL_FALSE;
1205 break;
1207 case TT_FUNCTION:
1208 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1209 != APPLY_FIX) {
1210 #ifdef DEBUG
1211 if (VLEVEL( VERB_EVERYTHING ))
1212 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1213 pz_fname, p_fixd->test_ct - test_ct);
1214 #endif
1215 return BOOL_FALSE;
1217 break;
1221 return BOOL_TRUE;
1225 /* * * * * * * * * * * * *
1227 Write out a replacement file */
1229 static void write_replacement PARAMS ((tFixDesc *));
1230 static void
1231 write_replacement (p_fixd)
1232 tFixDesc *p_fixd;
1234 const char* pz_text = p_fixd->patch_args[0];
1236 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1237 return;
1240 FILE* out_fp = create_file ();
1241 fputs (pz_text, out_fp);
1242 fclose (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));
1257 static void
1258 test_for_changes (read_fd)
1259 int 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;
1265 #ifdef DO_STATS
1266 fixed_ct++;
1267 #endif
1268 for (;;)
1270 int ch;
1272 ch = getc (in_fp);
1273 if (ch == EOF)
1274 break;
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)
1281 putc (ch, out_fp);
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 ();
1290 #ifdef DO_STATS
1291 altered_ct++;
1292 #endif
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),
1296 1, out_fp);
1298 /* Emit the current unmatching character */
1299 putc (ch, out_fp);
1301 else
1302 /* ELSE the character matches. Advance the compare ptr */
1303 pz_cmp++;
1306 /* IF we created the output file, ... */
1307 if (out_fp != (FILE *) NULL)
1309 regmatch_t match;
1311 /* Close the file and see if we have to worry about
1312 `#include "file.h"' constructs. */
1313 fclose (out_fp);
1314 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1315 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1318 fclose (in_fp);
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. */
1329 void
1330 process ()
1332 tFixDesc *p_fixd = fixDescList;
1333 int todo_ct = FIX_COUNT;
1334 int read_fd = -1;
1335 # ifndef SEPARATE_FIX_PROC
1336 int num_children = 0;
1337 # else /* is SEPARATE_FIX_PROC */
1338 char* pz_file_source = pz_curr_file;
1339 # endif
1341 if (access (pz_curr_file, R_OK) != 0)
1343 int erno = errno;
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));
1347 return;
1350 pz_curr_data = load_file (pz_curr_file);
1351 if (pz_curr_data == (char *) NULL)
1352 return;
1354 #ifdef DO_STATS
1355 process_ct++;
1356 #endif
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))
1368 continue;
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);
1377 UNLOAD_DATA();
1378 return;
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. */
1387 if (read_fd == -1)
1389 read_fd = open (pz_curr_file, O_RDONLY);
1390 if (read_fd < 0)
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 */
1399 fflush (stdout);
1402 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1403 num_children++;
1406 /* IF we have a read-back file descriptor,
1407 THEN check for changes and write output if changed. */
1409 if (read_fd >= 0)
1411 test_for_changes (read_fd);
1412 #ifdef DO_STATS
1413 apply_ct += num_children;
1414 #endif
1415 /* Wait for child processes created by chain_open()
1416 to avoid leaving zombies. */
1417 do {
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))
1427 continue;
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);
1436 UNLOAD_DATA();
1437 return;
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);
1444 if (read_fd < 0)
1446 if (errno != ENOENT)
1447 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1448 errno, xstrerror (errno), pz_temp_file);
1450 else
1452 test_for_changes (read_fd);
1453 /* Unlinking a file while it is still open is a Bad Idea on
1454 DOS/Windows. */
1455 close (read_fd);
1456 unlink (pz_temp_file);
1459 # endif
1460 UNLOAD_DATA();