Missed one in last change.
[official-gcc.git] / gcc / fixinc / fixincl.c
blob4a7248867553e34997d6f8ddc8ee017167719ce7
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)
12 any later version.
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. */
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 default:
288 case 'p':
289 case 'P':
290 verbose_level = VERB_PROGRESS; break;
292 case 't':
293 case 'T':
294 verbose_level = VERB_TESTS; break;
296 case 'e':
297 case 'E':
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] == '/'))
305 pz_find_base += 2;
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.
312 run_compiles ();
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" );
318 # endif
320 signal (SIGQUIT, SIG_IGN);
321 #ifdef SIGIOT
322 signal (SIGIOT, SIG_IGN);
323 #endif
324 #ifdef SIGPIPE
325 signal (SIGPIPE, SIG_IGN);
326 #endif
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. */
337 char *
338 load_file ( fname )
339 const char* fname;
341 struct stat stbf;
342 char* res;
344 if (stat (fname, &stbf) != 0)
346 if (NOT_SILENT)
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)
352 return (char*)NULL;
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;
362 if (data_map_fd < 0)
364 if (NOT_SILENT)
365 fprintf (stderr, "error %d (%s) opening %s for read\n",
366 errno, xstrerror (errno), fname);
367 return (char*)NULL;
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;
377 else
378 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
379 MAP_PRIVATE, data_map_fd, 0);
380 if (res == (char*)BAD_ADDR)
381 #endif
383 FILE* fp = fdopen (data_map_fd, "r");
384 curr_data_mapped = BOOL_FALSE;
385 res = load_file_data (fp);
386 fclose (fp);
389 return res;
392 static int machine_matches PARAMS ((tFixDesc *));
393 static int
394 machine_matches( p_fixd )
395 tFixDesc *p_fixd;
397 # ifndef SEPARATE_FIX_PROC
398 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
399 tSCC esac_fmt[] =
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;
406 char *pz;
407 const char *pz_sep = "";
408 tCC *pz_if_true;
409 tCC *pz_if_false;
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)
421 pz_if_true = skip;
422 pz_if_false = run;
424 else
426 pz_if_true = run;
427 pz_if_false = skip;
430 /* Emit all the machine names. If there are more than one,
431 then we will insert " | \\\n" between the names */
433 for (;;)
435 const char* pz_mach = *(papz_machs++);
437 if (pz_mach == (const char*) NULL)
438 break;
439 sprintf (pz, "%s%s", pz_sep, pz_mach);
440 pz += strlen (pz);
441 pz_sep = " | \\\n";
444 /* Now emit the match and not-match actions and the esac */
446 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
448 /* Run the script.
449 The result will start either with 's' or 'r'. */
452 int skip;
453 pz = run_shell (cmd_buf);
454 skip = (*pz == 's');
455 free ( (void*)pz );
456 if (skip)
458 p_fixd->fd_flags |= FD_SKIP_TEST;
459 return BOOL_FALSE;
463 return BOOL_TRUE;
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;
467 for (;;)
469 const char* pz_mach = *(papz_machs++);
471 if (pz_mach == (const char*) NULL)
472 break;
473 if (strstr (pz_mach, "dos") != NULL && !invert)
474 return BOOL_TRUE;
477 p_fixd->fd_flags |= FD_SKIP_TEST;
478 return BOOL_FALSE;
479 # endif
482 /* * * * * * * * * * * * *
484 run_compiles run all the regexp compiles for all the fixes once.
486 void
487 run_compiles ()
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
514 THEN ... */
516 if ( (pz_machine != NULL)
517 && (p_fixd->papz_machs != (const char**) NULL)
518 && ! machine_matches (p_fixd) )
519 continue;
521 /* FOR every test for the fixup, ... */
523 while (--test_ct >= 0)
525 switch (p_test->type)
527 case TT_EGREP:
528 case TT_NEGREP:
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);
532 default: break;
534 p_test++;
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)
551 #else
552 # define S_IRALL 0644
553 #endif
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)
559 #else
560 # define S_DIRALL 0755
561 #endif
564 static FILE *create_file PARAMS ((void));
565 static FILE *
566 create_file ()
568 int fd;
569 FILE *pf;
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, '/');
580 struct stat stbf;
582 while (pz_dir != (char *) NULL)
584 *pz_dir = NUL;
585 if (stat (fname, &stbf) < 0)
587 mkdir (fname, S_IFDIR | S_DIRALL);
590 *pz_dir = '/';
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);
597 if (fd < 0)
599 fprintf (stderr, "Error %d (%s) creating %s\n",
600 errno, xstrerror (errno), fname);
601 exit (EXIT_FAILURE);
603 if (NOT_SILENT)
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,
612 (pz_machine == NULL)
613 ? "fixinc/tests/inc"
614 : pz_input_dir,
615 pz_curr_file);
617 return pf;
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 *));
630 static int
631 test_test (p_test, pz_test_file)
632 tTestDesc *p_test;
633 char* pz_test_file;
635 tSCC cmd_fmt[] =
636 "file=%s\n\
637 if ( test %s ) > /dev/null 2>&1\n\
638 then echo TRUE\n\
639 else echo FALSE\n\
640 fi";
642 char *pz_res;
643 int res;
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);
650 switch (*pz_res) {
651 case 'T':
652 res = APPLY_FIX;
653 break;
655 case 'F':
656 res = SKIP_FIX;
657 break;
659 default:
660 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
661 pz_res, cmd_buf );
664 free ((void *) pz_res);
665 return res;
667 #else
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
673 #endif
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
683 is inverted. */
685 static int egrep_test PARAMS ((char *, tTestDesc *));
686 static int
687 egrep_test (pz_data, p_test)
688 char *pz_data;
689 tTestDesc *p_test;
691 #ifdef DEBUG
692 if (p_test->p_test_regex == 0)
693 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
694 p_test->pz_test_text);
695 #endif
696 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
697 return APPLY_FIX;
698 return SKIP_FIX;
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 *));
709 static int
710 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
711 const char *pz_src_path;
712 const char *pz_file_path;
713 const char *pz_file;
715 char z[ MAXPATHLEN ];
716 char* pz;
717 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
718 pz = z + strlen ( z );
720 for (;;) {
721 char ch = *pz_file++;
722 if (! ISGRAPH( ch ))
723 return 0;
724 if (ch == '"')
725 break;
726 *pz++ = ch;
728 *pz = '\0';
730 struct stat s;
731 if (stat (z, &s) != 0)
732 return 0;
733 return S_ISREG( s.st_mode );
738 /* * * * * * * * * * * * *
740 extract_quoted_files
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
748 emits a triple of:
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 *));
761 static void
762 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
763 char *pz_data;
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)
777 pz_fixed_file = ".";
778 else
779 *pz_dir_end = '\0';
781 for (;;)
783 pz_incl_quot += p_re_match->rm_so;
785 /* Skip forward to the included file name */
786 while (*pz_incl_quot != '"')
787 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);
806 /* End of entry */
807 putc ('\n', stdout);
810 /* Find the next entry */
811 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
812 break;
817 /* * * * * * * * * * * * *
819 Somebody wrote a *_fix subroutine that we must call.
821 #ifndef SEPARATE_FIX_PROC
822 static int internal_fix PARAMS ((int, tFixDesc *));
823 static int
824 internal_fix (read_fd, p_fixd)
825 int read_fd;
826 tFixDesc* p_fixd;
828 int fd[2];
830 if (pipe( fd ) != 0)
832 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
833 exit (EXIT_FAILURE);
836 for (;;)
838 pid_t childid = fork();
840 switch (childid)
842 case -1:
843 break;
845 case 0:
846 close (fd[0]);
847 goto do_child_task;
849 default:
851 * Parent process
853 close (read_fd);
854 close (fd[1]);
855 return fd[0];
859 * Parent in error
861 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
862 p_fixd->fix_name);
864 static int failCt = 0;
865 if ((errno != EAGAIN) || (++failCt > 10))
866 exit (EXIT_FAILURE);
867 sleep (1);
869 } do_child_task:;
872 * Close our current stdin and stdout
874 close (STDIN_FILENO);
875 close (STDOUT_FILENO);
876 UNLOAD_DATA();
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);
886 exit (0);
888 #endif /* !SEPARATE_FIX_PROC */
891 #ifdef SEPARATE_FIX_PROC
892 static void
893 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
894 tFixDesc* p_fixd;
895 tCC* pz_fix_file;
896 tCC* pz_file_source;
897 tCC* pz_temp_file;
899 char* pz_cmd;
900 char* pz_scan;
901 size_t argsize;
903 if (p_fixd->fd_flags & FD_SUBROUTINE)
905 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
907 argsize = 32
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;
920 *(pz_scan++) = ' ';
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: */
930 size_t parg_size;
931 #ifdef __MSDOS__
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\'";
945 #else
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";
949 #endif
950 tCC** ppArgs = p_fixd->patch_args;
952 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
953 + strlen( pz_file_source );
954 parg_size = argsize;
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. :)
964 for (;;)
966 tCC* p_arg = *(ppArgs++);
967 if (p_arg == NULL)
968 break;
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
975 arguments. */
976 parg_size = argsize - parg_size;
978 ppArgs = p_fixd->patch_args;
981 * Copy the program name, unquoted
984 tCC* pArg = *(ppArgs++);
985 for (;;)
987 char ch = *(pArg++);
988 if (ch == NUL)
989 break;
990 *(pz_scan++) = ch;
995 * Copy the program arguments, quoted
997 for (;;)
999 tCC* pArg = *(ppArgs++);
1000 char* pz_scan_save;
1001 if (pArg == NULL)
1002 break;
1003 *(pz_scan++) = ' ';
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
1008 * size estimation.
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;
1015 parg_size += 100;
1016 pz_scan = make_raw_shell_str( pz_scan, pArg,
1017 parg_size - (pz_scan - pz_cmd) );
1022 * add the file machinations.
1024 #ifdef __MSDOS__
1025 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1026 #else
1027 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1028 pz_temp_file, pz_temp_file, pz_temp_file);
1029 #endif
1031 system( pz_cmd );
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
1040 for stdout. */
1042 #else /* is *NOT* SEPARATE_FIX_PROC */
1043 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1044 static int
1045 start_fixer (read_fd, p_fixd, pz_fix_file)
1046 int read_fd;
1047 tFixDesc* p_fixd;
1048 char* pz_fix_file;
1050 tCC* pz_cmd_save;
1051 char* pz_cmd;
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;
1058 else
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.
1075 for (;;)
1077 static int failCt = 0;
1078 int fd;
1080 fd = chain_open (read_fd,
1081 (tCC **) p_fixd->patch_args,
1082 (process_chain_head == -1)
1083 ? &process_chain_head : (pid_t *) NULL);
1085 if (fd != -1)
1087 read_fd = fd;
1088 break;
1091 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1092 p_fixd->fix_name);
1094 if ((errno != EAGAIN) || (++failCt > 10))
1095 exit (EXIT_FAILURE);
1096 sleep (1);
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;
1107 return read_fd;
1109 #endif
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 *));
1119 static t_bool
1120 fix_applies (p_fixd)
1121 tFixDesc *p_fixd;
1123 const char *pz_fname = pz_curr_file;
1124 const char *pz_scan = p_fixd->file_list;
1125 int test_ct;
1126 tTestDesc *p_test;
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))
1135 return BOOL_FALSE;
1136 # else
1137 if (p_fixd->fd_flags & FD_SKIP_TEST)
1138 return BOOL_FALSE;
1139 # endif
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)
1146 size_t name_len;
1148 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1149 pz_fname += 2;
1150 name_len = strlen (pz_fname);
1152 for (;;)
1154 pz_scan = strstr (pz_scan + 1, pz_fname);
1155 /* IF we can't match the string at all,
1156 THEN bail */
1157 if (pz_scan == (char *) NULL)
1158 return BOOL_FALSE;
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] == '|'))
1164 break;
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;
1172 test_ct-- > 0;
1173 p_test++)
1175 switch (p_test->type)
1177 case TT_TEST:
1178 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1179 #ifdef DEBUG
1180 if (VLEVEL( VERB_EVERYTHING ))
1181 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1182 pz_fname, p_fixd->test_ct - test_ct);
1183 #endif
1184 return BOOL_FALSE;
1186 break;
1188 case TT_EGREP:
1189 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1190 #ifdef DEBUG
1191 if (VLEVEL( VERB_EVERYTHING ))
1192 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1193 pz_fname, p_fixd->test_ct - test_ct);
1194 #endif
1195 return BOOL_FALSE;
1197 break;
1199 case TT_NEGREP:
1200 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1201 #ifdef DEBUG
1202 if (VLEVEL( VERB_EVERYTHING ))
1203 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1204 pz_fname, p_fixd->test_ct - test_ct);
1205 #endif
1206 /* Negated sense */
1207 return BOOL_FALSE;
1209 break;
1211 case TT_FUNCTION:
1212 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1213 != APPLY_FIX) {
1214 #ifdef DEBUG
1215 if (VLEVEL( VERB_EVERYTHING ))
1216 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1217 pz_fname, p_fixd->test_ct - test_ct);
1218 #endif
1219 return BOOL_FALSE;
1221 break;
1225 return BOOL_TRUE;
1229 /* * * * * * * * * * * * *
1231 Write out a replacement file */
1233 static void write_replacement PARAMS ((tFixDesc *));
1234 static void
1235 write_replacement (p_fixd)
1236 tFixDesc *p_fixd;
1238 const char* pz_text = p_fixd->patch_args[0];
1240 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1241 return;
1244 FILE* out_fp = create_file ();
1245 fputs (pz_text, out_fp);
1246 fclose (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));
1261 static void
1262 test_for_changes (read_fd)
1263 int 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;
1269 #ifdef DO_STATS
1270 fixed_ct++;
1271 #endif
1272 for (;;)
1274 int ch;
1276 ch = getc (in_fp);
1277 if (ch == EOF)
1278 break;
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)
1285 putc (ch, out_fp);
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 ();
1294 #ifdef DO_STATS
1295 altered_ct++;
1296 #endif
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),
1300 1, out_fp);
1302 /* Emit the current unmatching character */
1303 putc (ch, out_fp);
1305 else
1306 /* ELSE the character matches. Advance the compare ptr */
1307 pz_cmp++;
1310 /* IF we created the output file, ... */
1311 if (out_fp != (FILE *) NULL)
1313 regmatch_t match;
1315 /* Close the file and see if we have to worry about
1316 `#include "file.h"' constructs. */
1317 fclose (out_fp);
1318 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1319 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1322 fclose (in_fp);
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. */
1333 void
1334 process ()
1336 tFixDesc *p_fixd = fixDescList;
1337 int todo_ct = FIX_COUNT;
1338 int read_fd = -1;
1339 # ifndef SEPARATE_FIX_PROC
1340 int num_children = 0;
1341 # else /* is SEPARATE_FIX_PROC */
1342 char* pz_file_source = pz_curr_file;
1343 # endif
1345 if (access (pz_curr_file, R_OK) != 0)
1347 int erno = errno;
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));
1351 return;
1354 pz_curr_data = load_file (pz_curr_file);
1355 if (pz_curr_data == (char *) NULL)
1356 return;
1358 #ifdef DO_STATS
1359 process_ct++;
1360 #endif
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))
1372 continue;
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);
1381 UNLOAD_DATA();
1382 return;
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. */
1391 if (read_fd == -1)
1393 read_fd = open (pz_curr_file, O_RDONLY);
1394 if (read_fd < 0)
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 */
1403 fflush (stdout);
1406 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1407 num_children++;
1410 /* IF we have a read-back file descriptor,
1411 THEN check for changes and write output if changed. */
1413 if (read_fd >= 0)
1415 test_for_changes (read_fd);
1416 #ifdef DO_STATS
1417 apply_ct += num_children;
1418 #endif
1419 /* Wait for child processes created by chain_open()
1420 to avoid leaving zombies. */
1421 do {
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))
1431 continue;
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);
1440 UNLOAD_DATA();
1441 return;
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);
1448 if (read_fd < 0)
1450 if (errno != ENOENT)
1451 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1452 errno, xstrerror (errno), pz_temp_file);
1454 else
1456 test_for_changes (read_fd);
1457 /* Unlinking a file while it is still open is a Bad Idea on
1458 DOS/Windows. */
1459 close (read_fd);
1460 unlink (pz_temp_file);
1463 # endif
1464 UNLOAD_DATA();