Daily bump.
[official-gcc.git] / gcc / fixinc / fixincl.c
blobddc2f9577801d44bc18d482184c827b1cc31c97b
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 #include <signal.h>
32 #if ! defined( SIGCHLD ) && defined( SIGCLD )
33 # define SIGCHLD SIGCLD
34 #endif
35 #ifndef SEPARATE_FIX_PROC
36 #include "server.h"
37 #endif
39 /* The contents of this string are not very important. It is mostly
40 just used as part of the "I am alive and working" test. */
42 static const char program_id[] = "fixincl version 1.1";
44 /* This format will be used at the start of every generated file */
46 static const char z_std_preamble[] =
47 "/* DO NOT EDIT THIS FILE.\n\n\
48 It has been auto-edited by fixincludes from:\n\n\
49 \t\"%s/%s\"\n\n\
50 This had to be done to correct non-standard usages in the\n\
51 original, manufacturer supplied header file. */\n\n";
53 /* Working environment strings. Essentially, invocation 'options'. */
55 #define _ENV_(v,m,n,t) tCC* v = NULL;
56 ENV_TABLE
57 #undef _ENV_
59 int find_base_len = 0;
61 typedef enum {
62 VERB_SILENT = 0,
63 VERB_FIXES,
64 VERB_APPLIES,
65 VERB_PROGRESS,
66 VERB_TESTS,
67 VERB_EVERYTHING
68 } te_verbose;
70 te_verbose verbose_level = VERB_PROGRESS;
71 int have_tty = 0;
73 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
74 #define NOT_SILENT VLEVEL(VERB_FIXES)
76 pid_t process_chain_head = (pid_t) -1;
78 char* pz_curr_file; /* name of the current file under test/fix */
79 char* pz_curr_data; /* original contents of that file */
80 char* pz_temp_file; /* for DOS, a place to stash the temporary
81 fixed data between system(3) calls */
82 t_bool curr_data_mapped;
83 int data_map_fd;
84 size_t data_map_size;
85 size_t ttl_data_size = 0;
87 #ifdef DO_STATS
88 int process_ct = 0;
89 int apply_ct = 0;
90 int fixed_ct = 0;
91 int altered_ct = 0;
92 #endif /* DO_STATS */
94 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
95 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
96 regex_t incl_quote_re;
98 static void do_version PARAMS((void)) ATTRIBUTE_NORETURN;
99 char *load_file PARAMS((const char *));
100 void run_compiles PARAMS((void));
101 void initialize PARAMS((int argc,char** argv));
102 void process PARAMS((void));
104 /* External Source Code */
106 #include "fixincl.x"
108 /* * * * * * * * * * * * * * * * * * *
110 * MAIN ROUTINE
112 extern int main PARAMS ((int, char **));
114 main (argc, argv)
115 int argc;
116 char **argv;
118 char *file_name_buf;
120 initialize ( argc, argv );
122 have_tty = isatty (fileno (stderr));
124 /* Before anything else, ensure we can allocate our file name buffer. */
125 file_name_buf = load_file_data (stdin);
127 /* Because of the way server shells work, you have to keep stdin, out
128 and err open so that the proper input file does not get closed
129 by accident */
131 freopen ("/dev/null", "r", stdin);
133 if (file_name_buf == (char *) NULL)
135 fputs ("No file names listed for fixing\n", stderr);
136 exit (EXIT_FAILURE);
139 for (;;)
141 char* pz_end;
143 /* skip to start of name, past any "./" prefixes */
145 while (ISSPACE (*file_name_buf)) file_name_buf++;
146 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
147 file_name_buf += 2;
149 /* Check for end of list */
151 if (*file_name_buf == NUL)
152 break;
154 /* Set global file name pointer and find end of name */
156 pz_curr_file = file_name_buf;
157 pz_end = strchr( pz_curr_file, '\n' );
158 if (pz_end == (char*)NULL)
159 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
160 else
161 file_name_buf = pz_end + 1;
163 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
165 /* IF no name is found (blank line) or comment marker, skip line */
167 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
168 continue;
169 *pz_end = NUL;
171 process ();
172 } /* for (;;) */
174 #ifdef DO_STATS
175 if (VLEVEL( VERB_PROGRESS )) {
176 tSCC zFmt[] =
178 Processed %5d files containing %d bytes \n\
179 Applying %5d fixes to %d files\n\
180 Altering %5d of them\n";
182 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
183 fixed_ct, altered_ct);
185 #endif /* DO_STATS */
187 # ifdef SEPARATE_FIX_PROC
188 unlink( pz_temp_file );
189 # endif
190 exit (EXIT_SUCCESS);
194 static void
195 do_version ()
197 static const char zFmt[] = "echo '%s'";
198 char zBuf[ 1024 ];
200 /* The 'version' option is really used to test that:
201 1. The program loads correctly (no missing libraries)
202 2. that we can compile all the regular expressions.
203 3. we can correctly run our server shell process
205 run_compiles ();
206 sprintf (zBuf, zFmt, program_id);
207 #ifndef SEPARATE_FIX_PROC
208 puts (zBuf + 5);
209 exit (strcmp (run_shell (zBuf), program_id));
210 #else
211 exit (system (zBuf));
212 #endif
215 /* * * * * * * * * * * * */
217 void
218 initialize ( argc, argv )
219 int argc;
220 char** argv;
222 static const char var_not_found[] =
223 #ifndef __STDC__
224 "fixincl ERROR: %s environment variable not defined\n"
225 #else
226 "fixincl ERROR: %s environment variable not defined\n"
227 "each of these must be defined:\n"
228 # define _ENV_(vv,mm,nn,tt) "\t" nn " - " tt "\n"
229 ENV_TABLE
230 # undef _ENV_
231 #endif
234 xmalloc_set_program_name (argv[0]);
236 switch (argc)
238 case 1:
239 break;
241 case 2:
242 if (strcmp (argv[1], "-v") == 0)
243 do_version ();
244 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
246 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
247 errno, xstrerror (errno), argv[1] );
248 exit (EXIT_FAILURE);
250 break;
252 default:
253 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
254 exit (EXIT_FAILURE);
257 #ifdef SIGCHLD
258 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
259 receive the signal. A different setting is inheritable */
260 signal (SIGCHLD, SIG_DFL);
261 #endif
263 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
264 v = getenv (var); if (m && (v == NULL)) { \
265 fprintf (stderr, var_not_found, var); \
266 exit (EXIT_FAILURE); } }
268 ENV_TABLE
270 #undef _ENV_
272 if (ISDIGIT ( *pz_verbose ))
273 verbose_level = (te_verbose)atoi( pz_verbose );
274 else
275 switch (*pz_verbose) {
276 case 's':
277 case 'S':
278 verbose_level = VERB_SILENT; break;
280 case 'f':
281 case 'F':
282 verbose_level = VERB_FIXES; break;
284 case 'a':
285 case 'A':
286 verbose_level = VERB_APPLIES; break;
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;
301 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
302 pz_find_base += 2;
303 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
304 find_base_len = strlen( pz_find_base );
306 /* Compile all the regular expressions now.
307 That way, it is done only once for the whole run.
309 run_compiles ();
311 # ifdef SEPARATE_FIX_PROC
312 /* NULL as the first argument to `tempnam' causes it to DTRT
313 wrt the temporary directory where the file will be created. */
314 pz_temp_file = tempnam( NULL, "fxinc" );
315 # endif
317 signal (SIGQUIT, SIG_IGN);
318 #ifdef SIGIOT
319 signal (SIGIOT, SIG_IGN);
320 #endif
321 #ifdef SIGPIPE
322 signal (SIGPIPE, SIG_IGN);
323 #endif
324 signal (SIGALRM, SIG_IGN);
325 signal (SIGTERM, SIG_IGN);
328 /* * * * * * * * * * * * *
330 load_file loads all the contents of a file into malloc-ed memory.
331 Its argument is the name of the file to read in; the returned
332 result is the NUL terminated contents of the file. The file
333 is presumed to be an ASCII text file containing no NULs. */
334 char *
335 load_file ( fname )
336 const char* fname;
338 struct stat stbf;
339 char* res;
341 if (stat (fname, &stbf) != 0)
343 if (NOT_SILENT)
344 fprintf (stderr, "error %d (%s) stat-ing %s\n",
345 errno, xstrerror (errno), fname );
346 return (char *) NULL;
348 if (stbf.st_size == 0)
349 return (char*)NULL;
351 /* Make the data map size one larger than the file size for documentation
352 purposes. Truth is that there will be a following NUL character if
353 the file size is not a multiple of the page size. If it is a multiple,
354 then this adjustment sometimes fails anyway. */
355 data_map_size = stbf.st_size+1;
356 data_map_fd = open (fname, O_RDONLY);
357 ttl_data_size += data_map_size-1;
359 if (data_map_fd < 0)
361 if (NOT_SILENT)
362 fprintf (stderr, "error %d (%s) opening %s for read\n",
363 errno, xstrerror (errno), fname);
364 return (char*)NULL;
367 #ifdef HAVE_MMAP_FILE
368 curr_data_mapped = BOOL_TRUE;
370 /* IF the file size is a multiple of the page size,
371 THEN sometimes you will seg fault trying to access a trailing byte */
372 if ((stbf.st_size & (getpagesize()-1)) == 0)
373 res = (char*)BAD_ADDR;
374 else
375 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
376 MAP_PRIVATE, data_map_fd, 0);
377 if (res == (char*)BAD_ADDR)
378 #endif
380 FILE* fp = fdopen (data_map_fd, "r");
381 curr_data_mapped = BOOL_FALSE;
382 res = load_file_data (fp);
383 fclose (fp);
386 return res;
389 static int machine_matches PARAMS ((tFixDesc *));
390 static int
391 machine_matches( p_fixd )
392 tFixDesc *p_fixd;
394 # ifndef SEPARATE_FIX_PROC
395 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
396 tSCC esac_fmt[] =
397 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
398 tSCC skip[] = "skip"; /* 4 bytes */
399 tSCC run[] = "run"; /* 3 bytes */
400 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
402 const char **papz_machs = p_fixd->papz_machs;
403 char *pz;
404 const char *pz_sep = "";
405 tCC *pz_if_true;
406 tCC *pz_if_false;
407 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
409 /* Start the case statement */
411 sprintf (cmd_buf, case_fmt, pz_machine);
412 pz = cmd_buf + strlen (cmd_buf);
414 /* Determine if a match means to apply the fix or not apply it */
416 if (p_fixd->fd_flags & FD_MACH_IFNOT)
418 pz_if_true = skip;
419 pz_if_false = run;
421 else
423 pz_if_true = run;
424 pz_if_false = skip;
427 /* Emit all the machine names. If there are more than one,
428 then we will insert " | \\\n" between the names */
430 for (;;)
432 const char* pz_mach = *(papz_machs++);
434 if (pz_mach == (const char*) NULL)
435 break;
436 sprintf (pz, "%s%s", pz_sep, pz_mach);
437 pz += strlen (pz);
438 pz_sep = " | \\\n";
441 /* Now emit the match and not-match actions and the esac */
443 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
445 /* Run the script.
446 The result will start either with 's' or 'r'. */
449 int skip;
450 pz = run_shell (cmd_buf);
451 skip = (*pz == 's');
452 free ( (void*)pz );
453 if (skip)
455 p_fixd->fd_flags |= FD_SKIP_TEST;
456 return BOOL_FALSE;
460 return BOOL_TRUE;
461 # else /* is SEPARATE_FIX_PROC */
462 const char **papz_machs = p_fixd->papz_machs;
463 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
464 for (;;)
466 const char* pz_mach = *(papz_machs++);
468 if (pz_mach == (const char*) NULL)
469 break;
470 if (strstr (pz_mach, "dos") != NULL && !invert)
471 return BOOL_TRUE;
474 p_fixd->fd_flags |= FD_SKIP_TEST;
475 return BOOL_FALSE;
476 # endif
479 /* * * * * * * * * * * * *
481 run_compiles run all the regexp compiles for all the fixes once.
483 void
484 run_compiles ()
486 tFixDesc *p_fixd = fixDescList;
487 int fix_ct = FIX_COUNT;
488 regex_t *p_re = (regex_t *) xmalloc (REGEX_COUNT * sizeof (regex_t));
490 /* Make sure compile_re does not stumble across invalid data */
492 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
493 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
495 compile_re (incl_quote_pat, &incl_quote_re, 1,
496 "quoted include", "run_compiles");
498 /* Allow machine name tests to be ignored (testing, mainly) */
500 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
501 pz_machine = (char*)NULL;
503 /* FOR every fixup, ... */
506 tTestDesc *p_test = p_fixd->p_test_desc;
507 int test_ct = p_fixd->test_ct;
509 /* IF the machine type pointer is not NULL (we are not in test mode)
510 AND this test is for or not done on particular machines
511 THEN ... */
513 if ( (pz_machine != NULL)
514 && (p_fixd->papz_machs != (const char**) NULL)
515 && ! machine_matches (p_fixd) )
516 continue;
518 /* FOR every test for the fixup, ... */
520 while (--test_ct >= 0)
522 switch (p_test->type)
524 case TT_EGREP:
525 case TT_NEGREP:
526 p_test->p_test_regex = p_re++;
527 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
528 "select test", p_fixd->fix_name);
529 default: break;
531 p_test++;
534 while (p_fixd++, --fix_ct > 0);
538 /* * * * * * * * * * * * *
540 create_file Create the output modified file.
541 Input: the name of the file to create
542 Returns: a file pointer to the new, open file */
544 #if defined(S_IRUSR) && defined(S_IWUSR) && \
545 defined(S_IRGRP) && defined(S_IROTH)
547 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
548 #else
549 # define S_IRALL 0644
550 #endif
552 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
553 defined(S_IROTH) && defined(S_IXOTH)
555 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
556 #else
557 # define S_DIRALL 0755
558 #endif
561 static FILE *create_file PARAMS ((void));
562 static FILE *
563 create_file ()
565 int fd;
566 FILE *pf;
567 char fname[MAXPATHLEN];
569 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
571 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
573 /* We may need to create the directories needed... */
574 if ((fd < 0) && (errno == ENOENT))
576 char *pz_dir = strchr (fname + 1, '/');
577 struct stat stbf;
579 while (pz_dir != (char *) NULL)
581 *pz_dir = NUL;
582 if (stat (fname, &stbf) < 0)
584 mkdir (fname, S_IFDIR | S_DIRALL);
587 *pz_dir = '/';
588 pz_dir = strchr (pz_dir + 1, '/');
591 /* Now, lets try the open again... */
592 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
594 if (fd < 0)
596 fprintf (stderr, "Error %d (%s) creating %s\n",
597 errno, xstrerror (errno), fname);
598 exit (EXIT_FAILURE);
600 if (NOT_SILENT)
601 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
602 pf = fdopen (fd, "w");
605 * IF pz_machine is NULL, then we are in some sort of test mode.
606 * Do not insert the current directory name. Use a constant string.
608 fprintf (pf, z_std_preamble,
609 (pz_machine == NULL)
610 ? "fixinc/tests/inc"
611 : pz_input_dir,
612 pz_curr_file);
614 return pf;
618 /* * * * * * * * * * * * *
620 test_test make sure a shell-style test expression passes.
621 Input: a pointer to the descriptor of the test to run and
622 the name of the file that we might want to fix
623 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
624 shell script we run. */
625 #ifndef SEPARATE_FIX_PROC
626 static int test_test PARAMS ((tTestDesc *, char *));
627 static int
628 test_test (p_test, pz_test_file)
629 tTestDesc *p_test;
630 char* pz_test_file;
632 tSCC cmd_fmt[] =
633 "file=%s\n\
634 if ( test %s ) > /dev/null 2>&1\n\
635 then echo TRUE\n\
636 else echo FALSE\n\
637 fi";
639 char *pz_res;
640 int res;
642 static char cmd_buf[4096];
644 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
645 pz_res = run_shell (cmd_buf);
647 switch (*pz_res) {
648 case 'T':
649 res = APPLY_FIX;
650 break;
652 case 'F':
653 res = SKIP_FIX;
654 break;
656 default:
657 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
658 pz_res, cmd_buf );
661 free ((void *) pz_res);
662 return res;
664 #else
666 * IF we are in MS-DOS land, then whatever shell-type test is required
667 * will, by definition, fail
669 #define test_test(t,tf) SKIP_FIX
670 #endif
672 /* * * * * * * * * * * * *
674 egrep_test make sure an egrep expression is found in the file text.
675 Input: a pointer to the descriptor of the test to run and
676 the pointer to the contents of the file under suspicion
677 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
679 The caller may choose to reverse meaning if the sense of the test
680 is inverted. */
682 static int egrep_test PARAMS ((char *, tTestDesc *));
683 static int
684 egrep_test (pz_data, p_test)
685 char *pz_data;
686 tTestDesc *p_test;
688 #ifdef DEBUG
689 if (p_test->p_test_regex == 0)
690 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
691 p_test->pz_test_text);
692 #endif
693 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
694 return APPLY_FIX;
695 return SKIP_FIX;
699 /* * * * * * * * * * * * *
701 quoted_file_exists Make sure that a file exists before we emit
702 the file name. If we emit the name, our invoking shell will try
703 to copy a non-existing file into the destination directory. */
705 static int quoted_file_exists PARAMS ((const char *, const char *, const char *));
706 static int
707 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
708 const char *pz_src_path;
709 const char *pz_file_path;
710 const char *pz_file;
712 char z[ MAXPATHLEN ];
713 char* pz;
714 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
715 pz = z + strlen ( z );
717 for (;;) {
718 char ch = *pz_file++;
719 if (! ISGRAPH( ch ))
720 return 0;
721 if (ch == '"')
722 break;
723 *pz++ = ch;
725 *pz = '\0';
727 struct stat s;
728 if (stat (z, &s) != 0)
729 return 0;
730 return S_ISREG( s.st_mode );
735 /* * * * * * * * * * * * *
737 extract_quoted_files
739 The syntax, `#include "file.h"' specifies that the compiler is to
740 search the local directory of the current file before the include
741 list. Consequently, if we have modified a header and stored it in
742 another directory, any files that are included by that modified
743 file in that fashion must also be copied into this new directory.
744 This routine finds those flavors of #include and for each one found
745 emits a triple of:
747 1. source directory of the original file
748 2. the relative path file name of the #includ-ed file
749 3. the full destination path for this file
751 Input: the text of the file, the file name and a pointer to the
752 match list where the match information was stored.
753 Result: internally nothing. The results are written to stdout
754 for interpretation by the invoking shell */
757 static void extract_quoted_files PARAMS ((char *, const char *, regmatch_t *));
758 static void
759 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
760 char *pz_data;
761 const char *pz_fixed_file;
762 regmatch_t *p_re_match;
764 char *pz_dir_end = strrchr (pz_fixed_file, '/');
765 char *pz_incl_quot = pz_data;
767 if (VLEVEL( VERB_APPLIES ))
768 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
770 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
771 If there is none, then it is in our current directory, ".". */
773 if (pz_dir_end == (char *) NULL)
774 pz_fixed_file = ".";
775 else
776 *pz_dir_end = '\0';
778 for (;;)
780 pz_incl_quot += p_re_match->rm_so;
782 /* Skip forward to the included file name */
783 while (*pz_incl_quot != '"')
784 pz_incl_quot++;
786 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
788 /* Print the source directory and the subdirectory
789 of the file in question. */
790 printf ("%s %s/", pz_src_dir, pz_fixed_file);
791 pz_dir_end = pz_incl_quot;
793 /* Append to the directory the relative path of the desired file */
794 while (*pz_incl_quot != '"')
795 putc (*pz_incl_quot++, stdout);
797 /* Now print the destination directory appended with the
798 relative path of the desired file */
799 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
800 while (*pz_dir_end != '"')
801 putc (*pz_dir_end++, stdout);
803 /* End of entry */
804 putc ('\n', stdout);
807 /* Find the next entry */
808 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
809 break;
814 /* * * * * * * * * * * * *
816 Somebody wrote a *_fix subroutine that we must call.
818 #ifndef SEPARATE_FIX_PROC
819 static int internal_fix PARAMS ((int, tFixDesc *));
820 static int
821 internal_fix (read_fd, p_fixd)
822 int read_fd;
823 tFixDesc* p_fixd;
825 int fd[2];
827 if (pipe( fd ) != 0)
829 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
830 exit (EXIT_FAILURE);
833 for (;;)
835 pid_t childid = fork();
837 switch (childid)
839 case -1:
840 break;
842 case 0:
843 close (fd[0]);
844 goto do_child_task;
846 default:
848 * Parent process
850 close (read_fd);
851 close (fd[1]);
852 return fd[0];
856 * Parent in error
858 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
859 p_fixd->fix_name);
861 static int failCt = 0;
862 if ((errno != EAGAIN) || (++failCt > 10))
863 exit (EXIT_FAILURE);
864 sleep (1);
866 } do_child_task:;
869 * Close our current stdin and stdout
871 close (STDIN_FILENO);
872 close (STDOUT_FILENO);
873 UNLOAD_DATA();
876 * Make the fd passed in the stdin, and the write end of
877 * the new pipe become the stdout.
879 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
880 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
882 apply_fix (p_fixd, pz_curr_file);
883 exit (0);
885 #endif /* !SEPARATE_FIX_PROC */
888 #ifdef SEPARATE_FIX_PROC
889 static void
890 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
891 tFixDesc* p_fixd;
892 tCC* pz_fix_file;
893 tCC* pz_file_source;
894 tCC* pz_temp_file;
896 char* pz_cmd;
897 char* pz_scan;
898 size_t argsize;
900 if (p_fixd->fd_flags & FD_SUBROUTINE)
902 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
904 argsize = 32
905 + strlen( pz_orig_dir )
906 + sizeof( z_applyfix_prog )
907 + strlen( pz_fix_file )
908 + strlen( pz_file_source )
909 + strlen( pz_temp_file );
911 pz_cmd = (char*)xmalloc( argsize );
913 strcpy( pz_cmd, pz_orig_dir );
914 pz_scan = pz_cmd + strlen( pz_orig_dir );
915 strcpy( pz_scan, z_applyfix_prog );
916 pz_scan += sizeof( z_applyfix_prog ) - 1;
917 *(pz_scan++) = ' ';
920 * Now add the fix number and file names that may be needed
922 sprintf (pz_scan, "%ld \'%s\' \'%s\' \'%s\'", p_fixd - fixDescList,
923 pz_fix_file, pz_file_source, pz_temp_file);
925 else /* NOT an "internal" fix: */
927 size_t parg_size;
928 #ifdef __MSDOS__
929 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
930 dst is a temporary file anyway, so we know there's no other
931 file by that name; and DOS's system(3) doesn't mind to
932 clobber existing file in redirection. Besides, with DOS 8+3
933 limited file namespace, we can easily lose if dst already has
934 an extension that is 3 or more characters long.
936 I do not think the 8+3 issue is relevant because all the files
937 we operate on are named "*.h", making 8+2 adequate. Anyway,
938 the following bizarre use of 'cat' only works on DOS boxes.
939 It causes the file to be dropped into a temporary file for
940 'cat' to read (pipes do not work on DOS). */
941 tSCC z_cmd_fmt[] = " \'%s\' | cat > \'%s\'";
942 #else
943 /* Don't use positional formatting arguments because some lame-o
944 implementations cannot cope :-(. */
945 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
946 #endif
947 tCC** ppArgs = p_fixd->patch_args;
949 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
950 + strlen( pz_file_source );
951 parg_size = argsize;
955 * Compute the size of the command line. Add lotsa extra space
956 * because some of the args to sed use lotsa single quotes.
957 * (This requires three extra bytes per quote. Here we allow
958 * for up to 8 single quotes for each argument, including the
959 * command name "sed" itself. Nobody will *ever* need more. :)
961 for (;;)
963 tCC* p_arg = *(ppArgs++);
964 if (p_arg == NULL)
965 break;
966 argsize += 24 + strlen( p_arg );
969 /* Estimated buffer size we will need. */
970 pz_scan = pz_cmd = (char*)xmalloc( argsize );
971 /* How much of it do we allot to the program name and its
972 arguments. */
973 parg_size = argsize - parg_size;
975 ppArgs = p_fixd->patch_args;
978 * Copy the program name, unquoted
981 tCC* pArg = *(ppArgs++);
982 for (;;)
984 char ch = *(pArg++);
985 if (ch == NUL)
986 break;
987 *(pz_scan++) = ch;
992 * Copy the program arguments, quoted
994 for (;;)
996 tCC* pArg = *(ppArgs++);
997 char* pz_scan_save;
998 if (pArg == NULL)
999 break;
1000 *(pz_scan++) = ' ';
1001 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
1002 parg_size - (pz_scan - pz_cmd) );
1004 * Make sure we don't overflow the buffer due to sloppy
1005 * size estimation.
1007 while (pz_scan == (char*)NULL)
1009 size_t already_filled = pz_scan_save - pz_cmd;
1010 pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
1011 pz_scan_save = pz_scan = pz_cmd + already_filled;
1012 parg_size += 100;
1013 pz_scan = make_raw_shell_str( pz_scan, pArg,
1014 parg_size - (pz_scan - pz_cmd) );
1019 * add the file machinations.
1021 #ifdef __MSDOS__
1022 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1023 #else
1024 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1025 pz_temp_file, pz_temp_file, pz_temp_file);
1026 #endif
1028 system( pz_cmd );
1029 free( (void*)pz_cmd );
1032 /* * * * * * * * * * * * *
1034 This loop should only cycle for 1/2 of one loop.
1035 "chain_open" starts a process that uses "read_fd" as
1036 its stdin and returns the new fd this process will use
1037 for stdout. */
1039 #else /* is *NOT* SEPARATE_FIX_PROC */
1040 static int start_fixer PARAMS ((int, tFixDesc *, char *));
1041 static int
1042 start_fixer (read_fd, p_fixd, pz_fix_file)
1043 int read_fd;
1044 tFixDesc* p_fixd;
1045 char* pz_fix_file;
1047 tCC* pz_cmd_save;
1048 char* pz_cmd;
1050 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1051 return internal_fix (read_fd, p_fixd);
1053 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1054 pz_cmd = (char*)NULL;
1055 else
1057 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1058 pz_cmd = (char*) xmalloc (strlen (p_fixd->patch_args[2])
1059 + sizeof( z_cmd_fmt )
1060 + strlen( pz_fix_file ));
1061 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1062 pz_cmd_save = p_fixd->patch_args[2];
1063 p_fixd->patch_args[2] = pz_cmd;
1066 /* Start a fix process, handing off the previous read fd for its
1067 stdin and getting a new fd that reads from the fix process' stdout.
1068 We normally will not loop, but we will up to 10 times if we keep
1069 getting "EAGAIN" errors.
1072 for (;;)
1074 static int failCt = 0;
1075 int fd;
1077 fd = chain_open (read_fd,
1078 (tCC **) p_fixd->patch_args,
1079 (process_chain_head == -1)
1080 ? &process_chain_head : (pid_t *) NULL);
1082 if (fd != -1)
1084 read_fd = fd;
1085 break;
1088 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1089 p_fixd->fix_name);
1091 if ((errno != EAGAIN) || (++failCt > 10))
1092 exit (EXIT_FAILURE);
1093 sleep (1);
1096 /* IF we allocated a shell script command,
1097 THEN free it and restore the command format to the fix description */
1098 if (pz_cmd != (char*)NULL)
1100 free ((void*)pz_cmd);
1101 p_fixd->patch_args[2] = pz_cmd_save;
1104 return read_fd;
1106 #endif
1109 /* * * * * * * * * * * * *
1111 Process the potential fixes for a particular include file.
1112 Input: the original text of the file and the file's name
1113 Result: none. A new file may or may not be created. */
1115 static t_bool fix_applies PARAMS ((tFixDesc *));
1116 static t_bool
1117 fix_applies (p_fixd)
1118 tFixDesc *p_fixd;
1120 const char *pz_fname = pz_curr_file;
1121 const char *pz_scan = p_fixd->file_list;
1122 int test_ct;
1123 tTestDesc *p_test;
1125 # ifdef SEPARATE_FIX_PROC
1127 * There is only one fix that uses a shell script as of this writing.
1128 * I hope to nuke it anyway, it does not apply to DOS and it would
1129 * be painful to implement. Therefore, no "shell" fixes for DOS.
1131 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1132 return BOOL_FALSE;
1133 # else
1134 if (p_fixd->fd_flags & FD_SKIP_TEST)
1135 return BOOL_FALSE;
1136 # endif
1138 /* IF there is a file name restriction,
1139 THEN ensure the current file name matches one in the pattern */
1141 if (pz_scan != (char *) NULL)
1143 size_t name_len;
1145 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1146 pz_fname += 2;
1147 name_len = strlen (pz_fname);
1149 for (;;)
1151 pz_scan = strstr (pz_scan + 1, pz_fname);
1152 /* IF we can't match the string at all,
1153 THEN bail */
1154 if (pz_scan == (char *) NULL)
1155 return BOOL_FALSE;
1157 /* IF the match is surrounded by the '|' markers,
1158 THEN we found a full match -- time to run the tests */
1160 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1161 break;
1165 /* FOR each test, see if it fails.
1166 IF it does fail, then we go on to the next test */
1168 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1169 test_ct-- > 0;
1170 p_test++)
1172 switch (p_test->type)
1174 case TT_TEST:
1175 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1176 #ifdef DEBUG
1177 if (VLEVEL( VERB_EVERYTHING ))
1178 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1179 pz_fname, p_fixd->test_ct - test_ct);
1180 #endif
1181 return BOOL_FALSE;
1183 break;
1185 case TT_EGREP:
1186 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1187 #ifdef DEBUG
1188 if (VLEVEL( VERB_EVERYTHING ))
1189 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1190 pz_fname, p_fixd->test_ct - test_ct);
1191 #endif
1192 return BOOL_FALSE;
1194 break;
1196 case TT_NEGREP:
1197 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1198 #ifdef DEBUG
1199 if (VLEVEL( VERB_EVERYTHING ))
1200 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1201 pz_fname, p_fixd->test_ct - test_ct);
1202 #endif
1203 /* Negated sense */
1204 return BOOL_FALSE;
1206 break;
1208 case TT_FUNCTION:
1209 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1210 != APPLY_FIX) {
1211 #ifdef DEBUG
1212 if (VLEVEL( VERB_EVERYTHING ))
1213 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1214 pz_fname, p_fixd->test_ct - test_ct);
1215 #endif
1216 return BOOL_FALSE;
1218 break;
1222 return BOOL_TRUE;
1226 /* * * * * * * * * * * * *
1228 Write out a replacement file */
1230 static void write_replacement PARAMS ((tFixDesc *));
1231 static void
1232 write_replacement (p_fixd)
1233 tFixDesc *p_fixd;
1235 const char* pz_text = p_fixd->patch_args[0];
1237 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1238 return;
1241 FILE* out_fp = create_file ();
1242 fputs (pz_text, out_fp);
1243 fclose (out_fp);
1248 /* * * * * * * * * * * * *
1250 We have work to do. Read back in the output
1251 of the filtering chain. Compare each byte as we read it with
1252 the contents of the original file. As soon as we find any
1253 difference, we will create the output file, write out all
1254 the matched text and then copy any remaining data from the
1255 output of the filter chain.
1257 static void test_for_changes PARAMS ((int));
1258 static void
1259 test_for_changes (read_fd)
1260 int read_fd;
1262 FILE *in_fp = fdopen (read_fd, "r");
1263 FILE *out_fp = (FILE *) NULL;
1264 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1266 #ifdef DO_STATS
1267 fixed_ct++;
1268 #endif
1269 for (;;)
1271 int ch;
1273 ch = getc (in_fp);
1274 if (ch == EOF)
1275 break;
1276 ch &= 0xFF; /* all bytes are 8 bits */
1278 /* IF we are emitting the output
1279 THEN emit this character, too.
1281 if (out_fp != (FILE *) NULL)
1282 putc (ch, out_fp);
1284 /* ELSE if this character does not match the original,
1285 THEN now is the time to start the output.
1287 else if (ch != *pz_cmp)
1289 out_fp = create_file ();
1291 #ifdef DO_STATS
1292 altered_ct++;
1293 #endif
1294 /* IF there are matched data, write the matched part now. */
1295 if ((char*)pz_cmp != pz_curr_data)
1296 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
1297 1, out_fp);
1299 /* Emit the current unmatching character */
1300 putc (ch, out_fp);
1302 else
1303 /* ELSE the character matches. Advance the compare ptr */
1304 pz_cmp++;
1307 /* IF we created the output file, ... */
1308 if (out_fp != (FILE *) NULL)
1310 regmatch_t match;
1312 /* Close the file and see if we have to worry about
1313 `#include "file.h"' constructs. */
1314 fclose (out_fp);
1315 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1316 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1319 fclose (in_fp);
1320 close (read_fd); /* probably redundant, but I'm paranoid */
1324 /* * * * * * * * * * * * *
1326 Process the potential fixes for a particular include file.
1327 Input: the original text of the file and the file's name
1328 Result: none. A new file may or may not be created. */
1330 void
1331 process ()
1333 tFixDesc *p_fixd = fixDescList;
1334 int todo_ct = FIX_COUNT;
1335 int read_fd = -1;
1336 # ifndef SEPARATE_FIX_PROC
1337 int num_children = 0;
1338 # else /* is SEPARATE_FIX_PROC */
1339 char* pz_file_source = pz_curr_file;
1340 # endif
1342 if (access (pz_curr_file, R_OK) != 0)
1344 int erno = errno;
1345 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1346 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1347 erno, xstrerror (erno));
1348 return;
1351 pz_curr_data = load_file (pz_curr_file);
1352 if (pz_curr_data == (char *) NULL)
1353 return;
1355 #ifdef DO_STATS
1356 process_ct++;
1357 #endif
1358 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1359 fprintf (stderr, "%6d %-50s \r", 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();