* config/xtensa/lib2funcs.S: Fix whitespace.
[official-gcc.git] / gcc / fixinc / fixincl.c
blob2d24c17237cef90ef10964f1836c6d7f41d4f6e0
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 (void) ATTRIBUTE_NORETURN;
98 char *load_file (const char *);
99 void run_compiles (void);
100 void initialize (int argc, char** argv);
101 void process (void);
103 /* External Source Code */
105 #include "fixincl.x"
107 /* * * * * * * * * * * * * * * * * * *
109 * MAIN ROUTINE
111 extern int main (int, char **);
113 main (int argc, char** argv)
115 char *file_name_buf;
117 initialize ( argc, argv );
119 have_tty = isatty (fileno (stderr));
121 /* Before anything else, ensure we can allocate our file name buffer. */
122 file_name_buf = load_file_data (stdin);
124 /* Because of the way server shells work, you have to keep stdin, out
125 and err open so that the proper input file does not get closed
126 by accident */
128 freopen ("/dev/null", "r", stdin);
130 if (file_name_buf == (char *) NULL)
132 fputs ("No file names listed for fixing\n", stderr);
133 exit (EXIT_FAILURE);
136 for (;;)
138 char* pz_end;
140 /* skip to start of name, past any "./" prefixes */
142 while (ISSPACE (*file_name_buf)) file_name_buf++;
143 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
144 file_name_buf += 2;
146 /* Check for end of list */
148 if (*file_name_buf == NUL)
149 break;
151 /* Set global file name pointer and find end of name */
153 pz_curr_file = file_name_buf;
154 pz_end = strchr( pz_curr_file, '\n' );
155 if (pz_end == (char*)NULL)
156 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
157 else
158 file_name_buf = pz_end + 1;
160 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
162 /* IF no name is found (blank line) or comment marker, skip line */
164 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
165 continue;
166 *pz_end = NUL;
168 process ();
169 } /* for (;;) */
171 #ifdef DO_STATS
172 if (VLEVEL( VERB_PROGRESS )) {
173 tSCC zFmt[] =
175 Processed %5d files containing %d bytes \n\
176 Applying %5d fixes to %d files\n\
177 Altering %5d of them\n";
179 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
180 fixed_ct, altered_ct);
182 #endif /* DO_STATS */
184 # ifdef SEPARATE_FIX_PROC
185 unlink( pz_temp_file );
186 # endif
187 exit (EXIT_SUCCESS);
191 static void
192 do_version (void)
194 static const char zFmt[] = "echo '%s'";
195 char zBuf[ 1024 ];
197 /* The 'version' option is really used to test that:
198 1. The program loads correctly (no missing libraries)
199 2. that we can compile all the regular expressions.
200 3. we can correctly run our server shell process
202 run_compiles ();
203 sprintf (zBuf, zFmt, program_id);
204 #ifndef SEPARATE_FIX_PROC
205 puts (zBuf + 5);
206 exit (strcmp (run_shell (zBuf), program_id));
207 #else
208 exit (system (zBuf));
209 #endif
212 /* * * * * * * * * * * * */
214 void
215 initialize ( int argc, char** argv )
217 static const char var_not_found[] =
218 #ifndef __STDC__
219 "fixincl ERROR: %s environment variable not defined\n"
220 #else
221 "fixincl ERROR: %s environment variable not defined\n"
222 "each of these must be defined:\n"
223 # define _ENV_(vv,mm,nn,tt) "\t" nn " - " tt "\n"
224 ENV_TABLE
225 # undef _ENV_
226 #endif
229 xmalloc_set_program_name (argv[0]);
231 switch (argc)
233 case 1:
234 break;
236 case 2:
237 if (strcmp (argv[1], "-v") == 0)
238 do_version ();
239 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
241 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
242 errno, xstrerror (errno), argv[1] );
243 exit (EXIT_FAILURE);
245 break;
247 default:
248 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
249 exit (EXIT_FAILURE);
252 #ifdef SIGCHLD
253 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
254 receive the signal. A different setting is inheritable */
255 signal (SIGCHLD, SIG_DFL);
256 #endif
258 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
259 v = getenv (var); if (m && (v == NULL)) { \
260 fprintf (stderr, var_not_found, var); \
261 exit (EXIT_FAILURE); } }
263 ENV_TABLE
265 #undef _ENV_
267 if (ISDIGIT ( *pz_verbose ))
268 verbose_level = (te_verbose)atoi( pz_verbose );
269 else
270 switch (*pz_verbose) {
271 case 's':
272 case 'S':
273 verbose_level = VERB_SILENT; break;
275 case 'f':
276 case 'F':
277 verbose_level = VERB_FIXES; break;
279 case 'a':
280 case 'A':
281 verbose_level = VERB_APPLIES; break;
283 default:
284 case 'p':
285 case 'P':
286 verbose_level = VERB_PROGRESS; break;
288 case 't':
289 case 'T':
290 verbose_level = VERB_TESTS; break;
292 case 'e':
293 case 'E':
294 verbose_level = VERB_EVERYTHING; break;
296 if (verbose_level >= VERB_EVERYTHING) {
297 verbose_level = VERB_EVERYTHING;
298 fputs ("fixinc verbosity: EVERYTHING\n", stderr);
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 ( const char* fname )
336 struct stat stbf;
337 char* res;
339 if (stat (fname, &stbf) != 0)
341 if (NOT_SILENT)
342 fprintf (stderr, "error %d (%s) stat-ing %s\n",
343 errno, xstrerror (errno), fname );
344 return (char *) NULL;
346 if (stbf.st_size == 0)
347 return (char*)NULL;
349 /* Make the data map size one larger than the file size for documentation
350 purposes. Truth is that there will be a following NUL character if
351 the file size is not a multiple of the page size. If it is a multiple,
352 then this adjustment sometimes fails anyway. */
353 data_map_size = stbf.st_size+1;
354 data_map_fd = open (fname, O_RDONLY);
355 ttl_data_size += data_map_size-1;
357 if (data_map_fd < 0)
359 if (NOT_SILENT)
360 fprintf (stderr, "error %d (%s) opening %s for read\n",
361 errno, xstrerror (errno), fname);
362 return (char*)NULL;
365 #ifdef HAVE_MMAP_FILE
366 curr_data_mapped = BOOL_TRUE;
368 /* IF the file size is a multiple of the page size,
369 THEN sometimes you will seg fault trying to access a trailing byte */
370 if ((stbf.st_size & (getpagesize()-1)) == 0)
371 res = (char*)BAD_ADDR;
372 else
373 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
374 MAP_PRIVATE, data_map_fd, 0);
375 if (res == (char*)BAD_ADDR)
376 #endif
378 FILE* fp = fdopen (data_map_fd, "r");
379 curr_data_mapped = BOOL_FALSE;
380 res = load_file_data (fp);
381 fclose (fp);
384 return res;
387 static int
388 machine_matches( tFixDesc* p_fixd )
390 # ifndef SEPARATE_FIX_PROC
391 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
392 tSCC esac_fmt[] =
393 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
394 tSCC skip[] = "skip"; /* 4 bytes */
395 tSCC run[] = "run"; /* 3 bytes */
396 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
398 const char **papz_machs = p_fixd->papz_machs;
399 char *pz;
400 const char *pz_sep = "";
401 tCC *pz_if_true;
402 tCC *pz_if_false;
403 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
405 /* Start the case statement */
407 sprintf (cmd_buf, case_fmt, pz_machine);
408 pz = cmd_buf + strlen (cmd_buf);
410 /* Determine if a match means to apply the fix or not apply it */
412 if (p_fixd->fd_flags & FD_MACH_IFNOT)
414 pz_if_true = skip;
415 pz_if_false = run;
417 else
419 pz_if_true = run;
420 pz_if_false = skip;
423 /* Emit all the machine names. If there are more than one,
424 then we will insert " | \\\n" between the names */
426 for (;;)
428 const char* pz_mach = *(papz_machs++);
430 if (pz_mach == (const char*) NULL)
431 break;
432 sprintf (pz, "%s%s", pz_sep, pz_mach);
433 pz += strlen (pz);
434 pz_sep = " | \\\n";
437 /* Now emit the match and not-match actions and the esac */
439 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
441 /* Run the script.
442 The result will start either with 's' or 'r'. */
445 int skip;
446 pz = run_shell (cmd_buf);
447 skip = (*pz == 's');
448 free ( (void*)pz );
449 if (skip)
451 p_fixd->fd_flags |= FD_SKIP_TEST;
452 return BOOL_FALSE;
456 return BOOL_TRUE;
457 # else /* is SEPARATE_FIX_PROC */
458 const char **papz_machs = p_fixd->papz_machs;
459 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
460 for (;;)
462 const char* pz_mach = *(papz_machs++);
464 if (pz_mach == (const char*) NULL)
465 break;
466 if (strstr (pz_mach, "dos") != NULL && !invert)
467 return BOOL_TRUE;
470 p_fixd->fd_flags |= FD_SKIP_TEST;
471 return BOOL_FALSE;
472 # endif
475 /* * * * * * * * * * * * *
477 run_compiles run all the regexp compiles for all the fixes once.
479 void
480 run_compiles (void)
482 tFixDesc *p_fixd = fixDescList;
483 int fix_ct = FIX_COUNT;
484 regex_t *p_re = xmalloc (REGEX_COUNT * sizeof (regex_t));
486 /* Make sure compile_re does not stumble across invalid data */
488 memset (p_re, '\0', REGEX_COUNT * sizeof (regex_t));
489 memset (&incl_quote_re, '\0', sizeof (regex_t));
491 compile_re (incl_quote_pat, &incl_quote_re, 1,
492 "quoted include", "run_compiles");
494 /* Allow machine name tests to be ignored (testing, mainly) */
496 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
497 pz_machine = (char*)NULL;
499 /* FOR every fixup, ... */
502 tTestDesc *p_test = p_fixd->p_test_desc;
503 int test_ct = p_fixd->test_ct;
505 /* IF the machine type pointer is not NULL (we are not in test mode)
506 AND this test is for or not done on particular machines
507 THEN ... */
509 if ( (pz_machine != NULL)
510 && (p_fixd->papz_machs != (const char**) NULL)
511 && ! machine_matches (p_fixd) )
512 continue;
514 /* FOR every test for the fixup, ... */
516 while (--test_ct >= 0)
518 switch (p_test->type)
520 case TT_EGREP:
521 case TT_NEGREP:
522 p_test->p_test_regex = p_re++;
523 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
524 "select test", p_fixd->fix_name);
525 default: break;
527 p_test++;
530 while (p_fixd++, --fix_ct > 0);
534 /* * * * * * * * * * * * *
536 create_file Create the output modified file.
537 Input: the name of the file to create
538 Returns: a file pointer to the new, open file */
540 #if defined(S_IRUSR) && defined(S_IWUSR) && \
541 defined(S_IRGRP) && defined(S_IROTH)
543 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
544 #else
545 # define S_IRALL 0644
546 #endif
548 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
549 defined(S_IROTH) && defined(S_IXOTH)
551 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
552 #else
553 # define S_DIRALL 0755
554 #endif
557 static FILE *
558 create_file (void)
560 int fd;
561 FILE *pf;
562 char fname[MAXPATHLEN];
564 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
566 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
568 /* We may need to create the directories needed... */
569 if ((fd < 0) && (errno == ENOENT))
571 char *pz_dir = strchr (fname + 1, '/');
572 struct stat stbf;
574 while (pz_dir != (char *) NULL)
576 *pz_dir = NUL;
577 if (stat (fname, &stbf) < 0)
579 mkdir (fname, S_IFDIR | S_DIRALL);
582 *pz_dir = '/';
583 pz_dir = strchr (pz_dir + 1, '/');
586 /* Now, lets try the open again... */
587 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
589 if (fd < 0)
591 fprintf (stderr, "Error %d (%s) creating %s\n",
592 errno, xstrerror (errno), fname);
593 exit (EXIT_FAILURE);
595 if (NOT_SILENT)
596 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
597 pf = fdopen (fd, "w");
600 * IF pz_machine is NULL, then we are in some sort of test mode.
601 * Do not insert the current directory name. Use a constant string.
603 fprintf (pf, z_std_preamble,
604 (pz_machine == NULL)
605 ? "fixinc/tests/inc"
606 : pz_input_dir,
607 pz_curr_file);
609 return pf;
613 /* * * * * * * * * * * * *
615 test_test make sure a shell-style test expression passes.
616 Input: a pointer to the descriptor of the test to run and
617 the name of the file that we might want to fix
618 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
619 shell script we run. */
620 #ifndef SEPARATE_FIX_PROC
621 static int
622 test_test (tTestDesc* p_test, char* pz_test_file)
624 tSCC cmd_fmt[] =
625 "file=%s\n\
626 if ( test %s ) > /dev/null 2>&1\n\
627 then echo TRUE\n\
628 else echo FALSE\n\
629 fi";
631 char *pz_res;
632 int res;
634 static char cmd_buf[4096];
636 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
637 pz_res = run_shell (cmd_buf);
639 switch (*pz_res) {
640 case 'T':
641 res = APPLY_FIX;
642 break;
644 case 'F':
645 res = SKIP_FIX;
646 break;
648 default:
649 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
650 pz_res, cmd_buf );
653 free ((void *) pz_res);
654 return res;
656 #else
658 * IF we are in MS-DOS land, then whatever shell-type test is required
659 * will, by definition, fail
661 #define test_test(t,tf) SKIP_FIX
662 #endif
664 /* * * * * * * * * * * * *
666 egrep_test make sure an egrep expression is found in the file text.
667 Input: a pointer to the descriptor of the test to run and
668 the pointer to the contents of the file under suspicion
669 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
671 The caller may choose to reverse meaning if the sense of the test
672 is inverted. */
674 static int
675 egrep_test (char* pz_data, tTestDesc* p_test)
677 #ifdef DEBUG
678 if (p_test->p_test_regex == 0)
679 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
680 p_test->pz_test_text);
681 #endif
682 if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
683 return APPLY_FIX;
684 return SKIP_FIX;
688 /* * * * * * * * * * * * *
690 quoted_file_exists Make sure that a file exists before we emit
691 the file name. If we emit the name, our invoking shell will try
692 to copy a non-existing file into the destination directory. */
694 static int
695 quoted_file_exists (const char* pz_src_path,
696 const char* pz_file_path,
697 const char* pz_file)
699 char z[ MAXPATHLEN ];
700 char* pz;
701 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
702 pz = z + strlen ( z );
704 for (;;) {
705 char ch = *pz_file++;
706 if (! ISGRAPH( ch ))
707 return 0;
708 if (ch == '"')
709 break;
710 *pz++ = ch;
712 *pz = '\0';
714 struct stat s;
715 if (stat (z, &s) != 0)
716 return 0;
717 return S_ISREG( s.st_mode );
722 /* * * * * * * * * * * * *
724 extract_quoted_files
726 The syntax, `#include "file.h"' specifies that the compiler is to
727 search the local directory of the current file before the include
728 list. Consequently, if we have modified a header and stored it in
729 another directory, any files that are included by that modified
730 file in that fashion must also be copied into this new directory.
731 This routine finds those flavors of #include and for each one found
732 emits a triple of:
734 1. source directory of the original file
735 2. the relative path file name of the #includ-ed file
736 3. the full destination path for this file
738 Input: the text of the file, the file name and a pointer to the
739 match list where the match information was stored.
740 Result: internally nothing. The results are written to stdout
741 for interpretation by the invoking shell */
744 static void
745 extract_quoted_files (char* pz_data,
746 const char* pz_fixed_file,
747 regmatch_t* p_re_match)
749 char *pz_dir_end = strrchr (pz_fixed_file, '/');
750 char *pz_incl_quot = pz_data;
752 if (VLEVEL( VERB_APPLIES ))
753 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
755 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
756 If there is none, then it is in our current directory, ".". */
758 if (pz_dir_end == (char *) NULL)
759 pz_fixed_file = ".";
760 else
761 *pz_dir_end = '\0';
763 for (;;)
765 pz_incl_quot += p_re_match->rm_so;
767 /* Skip forward to the included file name */
768 while (*pz_incl_quot != '"')
769 pz_incl_quot++;
771 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
773 /* Print the source directory and the subdirectory
774 of the file in question. */
775 printf ("%s %s/", pz_src_dir, pz_fixed_file);
776 pz_dir_end = pz_incl_quot;
778 /* Append to the directory the relative path of the desired file */
779 while (*pz_incl_quot != '"')
780 putc (*pz_incl_quot++, stdout);
782 /* Now print the destination directory appended with the
783 relative path of the desired file */
784 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
785 while (*pz_dir_end != '"')
786 putc (*pz_dir_end++, stdout);
788 /* End of entry */
789 putc ('\n', stdout);
792 /* Find the next entry */
793 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
794 break;
799 /* * * * * * * * * * * * *
801 Somebody wrote a *_fix subroutine that we must call.
803 #ifndef SEPARATE_FIX_PROC
804 static int
805 internal_fix (int read_fd, tFixDesc* p_fixd)
807 int fd[2];
809 if (pipe( fd ) != 0)
811 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
812 exit (EXIT_FAILURE);
815 for (;;)
817 pid_t childid = fork();
819 switch (childid)
821 case -1:
822 break;
824 case 0:
825 close (fd[0]);
826 goto do_child_task;
828 default:
830 * Parent process
832 close (read_fd);
833 close (fd[1]);
834 return fd[0];
838 * Parent in error
840 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
841 p_fixd->fix_name);
843 static int failCt = 0;
844 if ((errno != EAGAIN) || (++failCt > 10))
845 exit (EXIT_FAILURE);
846 sleep (1);
848 } do_child_task:;
851 * Close our current stdin and stdout
853 close (STDIN_FILENO);
854 close (STDOUT_FILENO);
855 UNLOAD_DATA();
858 * Make the fd passed in the stdin, and the write end of
859 * the new pipe become the stdout.
861 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
862 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
864 apply_fix (p_fixd, pz_curr_file);
865 exit (0);
867 #endif /* !SEPARATE_FIX_PROC */
870 #ifdef SEPARATE_FIX_PROC
871 static void
872 fix_with_system (tFixDesc* p_fixd,
873 tCC* pz_fix_file,
874 tCC* pz_file_source,
875 tCC* pz_temp_file)
877 char* pz_cmd;
878 char* pz_scan;
879 size_t argsize;
881 if (p_fixd->fd_flags & FD_SUBROUTINE)
883 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
885 argsize = 32
886 + strlen( pz_orig_dir )
887 + sizeof( z_applyfix_prog )
888 + strlen( pz_fix_file )
889 + strlen( pz_file_source )
890 + strlen( pz_temp_file );
892 pz_cmd = xmalloc (argsize);
894 strcpy( pz_cmd, pz_orig_dir );
895 pz_scan = pz_cmd + strlen( pz_orig_dir );
896 strcpy( pz_scan, z_applyfix_prog );
897 pz_scan += sizeof( z_applyfix_prog ) - 1;
898 *(pz_scan++) = ' ';
901 * Now add the fix number and file names that may be needed
903 sprintf (pz_scan, "%ld \'%s\' \'%s\' \'%s\'", p_fixd - fixDescList,
904 pz_fix_file, pz_file_source, pz_temp_file);
906 else /* NOT an "internal" fix: */
908 size_t parg_size;
909 #ifdef __MSDOS__
910 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
911 dst is a temporary file anyway, so we know there's no other
912 file by that name; and DOS's system(3) doesn't mind to
913 clobber existing file in redirection. Besides, with DOS 8+3
914 limited file namespace, we can easily lose if dst already has
915 an extension that is 3 or more characters long.
917 I do not think the 8+3 issue is relevant because all the files
918 we operate on are named "*.h", making 8+2 adequate. Anyway,
919 the following bizarre use of 'cat' only works on DOS boxes.
920 It causes the file to be dropped into a temporary file for
921 'cat' to read (pipes do not work on DOS). */
922 tSCC z_cmd_fmt[] = " \'%s\' | cat > \'%s\'";
923 #else
924 /* Don't use positional formatting arguments because some lame-o
925 implementations cannot cope :-(. */
926 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
927 #endif
928 tCC** ppArgs = p_fixd->patch_args;
930 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
931 + strlen( pz_file_source );
932 parg_size = argsize;
936 * Compute the size of the command line. Add lotsa extra space
937 * because some of the args to sed use lotsa single quotes.
938 * (This requires three extra bytes per quote. Here we allow
939 * for up to 8 single quotes for each argument, including the
940 * command name "sed" itself. Nobody will *ever* need more. :)
942 for (;;)
944 tCC* p_arg = *(ppArgs++);
945 if (p_arg == NULL)
946 break;
947 argsize += 24 + strlen( p_arg );
950 /* Estimated buffer size we will need. */
951 pz_scan = pz_cmd = xmalloc (argsize);
952 /* How much of it do we allot to the program name and its
953 arguments. */
954 parg_size = argsize - parg_size;
956 ppArgs = p_fixd->patch_args;
959 * Copy the program name, unquoted
962 tCC* pArg = *(ppArgs++);
963 for (;;)
965 char ch = *(pArg++);
966 if (ch == NUL)
967 break;
968 *(pz_scan++) = ch;
973 * Copy the program arguments, quoted
975 for (;;)
977 tCC* pArg = *(ppArgs++);
978 char* pz_scan_save;
979 if (pArg == NULL)
980 break;
981 *(pz_scan++) = ' ';
982 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
983 parg_size - (pz_scan - pz_cmd) );
985 * Make sure we don't overflow the buffer due to sloppy
986 * size estimation.
988 while (pz_scan == (char*)NULL)
990 size_t already_filled = pz_scan_save - pz_cmd;
991 pz_cmd = xrealloc (pz_cmd, argsize += 100);
992 pz_scan_save = pz_scan = pz_cmd + already_filled;
993 parg_size += 100;
994 pz_scan = make_raw_shell_str( pz_scan, pArg,
995 parg_size - (pz_scan - pz_cmd) );
1000 * add the file machinations.
1002 #ifdef __MSDOS__
1003 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1004 #else
1005 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1006 pz_temp_file, pz_temp_file, pz_temp_file);
1007 #endif
1009 system( pz_cmd );
1010 free( (void*)pz_cmd );
1013 /* * * * * * * * * * * * *
1015 This loop should only cycle for 1/2 of one loop.
1016 "chain_open" starts a process that uses "read_fd" as
1017 its stdin and returns the new fd this process will use
1018 for stdout. */
1020 #else /* is *NOT* SEPARATE_FIX_PROC */
1021 static int
1022 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
1024 tCC* pz_cmd_save;
1025 char* pz_cmd;
1027 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1028 return internal_fix (read_fd, p_fixd);
1030 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1031 pz_cmd = (char*)NULL;
1032 else
1034 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1035 pz_cmd = xmalloc (strlen (p_fixd->patch_args[2])
1036 + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
1037 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1038 pz_cmd_save = p_fixd->patch_args[2];
1039 p_fixd->patch_args[2] = pz_cmd;
1042 /* Start a fix process, handing off the previous read fd for its
1043 stdin and getting a new fd that reads from the fix process' stdout.
1044 We normally will not loop, but we will up to 10 times if we keep
1045 getting "EAGAIN" errors.
1048 for (;;)
1050 static int failCt = 0;
1051 int fd;
1053 fd = chain_open (read_fd,
1054 (tCC **) p_fixd->patch_args,
1055 (process_chain_head == -1)
1056 ? &process_chain_head : (pid_t *) NULL);
1058 if (fd != -1)
1060 read_fd = fd;
1061 break;
1064 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1065 p_fixd->fix_name);
1067 if ((errno != EAGAIN) || (++failCt > 10))
1068 exit (EXIT_FAILURE);
1069 sleep (1);
1072 /* IF we allocated a shell script command,
1073 THEN free it and restore the command format to the fix description */
1074 if (pz_cmd != (char*)NULL)
1076 free ((void*)pz_cmd);
1077 p_fixd->patch_args[2] = pz_cmd_save;
1080 return read_fd;
1082 #endif
1085 /* * * * * * * * * * * * *
1087 Process the potential fixes for a particular include file.
1088 Input: the original text of the file and the file's name
1089 Result: none. A new file may or may not be created. */
1091 static t_bool
1092 fix_applies (tFixDesc* p_fixd)
1094 const char *pz_fname = pz_curr_file;
1095 const char *pz_scan = p_fixd->file_list;
1096 int test_ct;
1097 tTestDesc *p_test;
1099 # ifdef SEPARATE_FIX_PROC
1101 * There is only one fix that uses a shell script as of this writing.
1102 * I hope to nuke it anyway, it does not apply to DOS and it would
1103 * be painful to implement. Therefore, no "shell" fixes for DOS.
1105 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1106 return BOOL_FALSE;
1107 # else
1108 if (p_fixd->fd_flags & FD_SKIP_TEST)
1109 return BOOL_FALSE;
1110 # endif
1112 /* IF there is a file name restriction,
1113 THEN ensure the current file name matches one in the pattern */
1115 if (pz_scan != (char *) NULL)
1117 size_t name_len;
1119 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1120 pz_fname += 2;
1121 name_len = strlen (pz_fname);
1123 for (;;)
1125 pz_scan = strstr (pz_scan + 1, pz_fname);
1126 /* IF we can't match the string at all,
1127 THEN bail */
1128 if (pz_scan == (char *) NULL)
1129 return BOOL_FALSE;
1131 /* IF the match is surrounded by the '|' markers,
1132 THEN we found a full match -- time to run the tests */
1134 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1135 break;
1139 /* FOR each test, see if it fails.
1140 IF it does fail, then we go on to the next test */
1142 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1143 test_ct-- > 0;
1144 p_test++)
1146 switch (p_test->type)
1148 case TT_TEST:
1149 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1150 #ifdef DEBUG
1151 if (VLEVEL( VERB_EVERYTHING ))
1152 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1153 pz_fname, p_fixd->test_ct - test_ct);
1154 #endif
1155 return BOOL_FALSE;
1157 break;
1159 case TT_EGREP:
1160 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1161 #ifdef DEBUG
1162 if (VLEVEL( VERB_EVERYTHING ))
1163 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1164 pz_fname, p_fixd->test_ct - test_ct);
1165 #endif
1166 return BOOL_FALSE;
1168 break;
1170 case TT_NEGREP:
1171 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1172 #ifdef DEBUG
1173 if (VLEVEL( VERB_EVERYTHING ))
1174 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1175 pz_fname, p_fixd->test_ct - test_ct);
1176 #endif
1177 /* Negated sense */
1178 return BOOL_FALSE;
1180 break;
1182 case TT_FUNCTION:
1183 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1184 != APPLY_FIX) {
1185 #ifdef DEBUG
1186 if (VLEVEL( VERB_EVERYTHING ))
1187 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1188 pz_fname, p_fixd->test_ct - test_ct);
1189 #endif
1190 return BOOL_FALSE;
1192 break;
1196 return BOOL_TRUE;
1200 /* * * * * * * * * * * * *
1202 Write out a replacement file */
1204 static void
1205 write_replacement (tFixDesc* p_fixd)
1207 const char* pz_text = p_fixd->patch_args[0];
1209 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1210 return;
1213 FILE* out_fp = create_file ();
1214 fputs (pz_text, out_fp);
1215 fclose (out_fp);
1220 /* * * * * * * * * * * * *
1222 We have work to do. Read back in the output
1223 of the filtering chain. Compare each byte as we read it with
1224 the contents of the original file. As soon as we find any
1225 difference, we will create the output file, write out all
1226 the matched text and then copy any remaining data from the
1227 output of the filter chain.
1229 static void
1230 test_for_changes (int read_fd)
1232 FILE *in_fp = fdopen (read_fd, "r");
1233 FILE *out_fp = (FILE *) NULL;
1234 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1236 #ifdef DO_STATS
1237 fixed_ct++;
1238 #endif
1239 for (;;)
1241 int ch;
1243 ch = getc (in_fp);
1244 if (ch == EOF)
1245 break;
1246 ch &= 0xFF; /* all bytes are 8 bits */
1248 /* IF we are emitting the output
1249 THEN emit this character, too.
1251 if (out_fp != (FILE *) NULL)
1252 putc (ch, out_fp);
1254 /* ELSE if this character does not match the original,
1255 THEN now is the time to start the output.
1257 else if (ch != *pz_cmp)
1259 out_fp = create_file ();
1261 #ifdef DO_STATS
1262 altered_ct++;
1263 #endif
1264 /* IF there are matched data, write the matched part now. */
1265 if ((char*)pz_cmp != pz_curr_data)
1266 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
1267 1, out_fp);
1269 /* Emit the current unmatching character */
1270 putc (ch, out_fp);
1272 else
1273 /* ELSE the character matches. Advance the compare ptr */
1274 pz_cmp++;
1277 /* IF we created the output file, ... */
1278 if (out_fp != (FILE *) NULL)
1280 regmatch_t match;
1282 /* Close the file and see if we have to worry about
1283 `#include "file.h"' constructs. */
1284 fclose (out_fp);
1285 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1286 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1289 fclose (in_fp);
1290 close (read_fd); /* probably redundant, but I'm paranoid */
1294 /* * * * * * * * * * * * *
1296 Process the potential fixes for a particular include file.
1297 Input: the original text of the file and the file's name
1298 Result: none. A new file may or may not be created. */
1300 void
1301 process (void)
1303 tFixDesc *p_fixd = fixDescList;
1304 int todo_ct = FIX_COUNT;
1305 int read_fd = -1;
1306 # ifndef SEPARATE_FIX_PROC
1307 int num_children = 0;
1308 # else /* is SEPARATE_FIX_PROC */
1309 char* pz_file_source = pz_curr_file;
1310 # endif
1312 if (access (pz_curr_file, R_OK) != 0)
1314 int erno = errno;
1315 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1316 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1317 erno, xstrerror (erno));
1318 return;
1321 pz_curr_data = load_file (pz_curr_file);
1322 if (pz_curr_data == (char *) NULL)
1323 return;
1325 #ifdef DO_STATS
1326 process_ct++;
1327 #endif
1328 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1329 fprintf (stderr, "%6lu %-50s \r",
1330 (unsigned long) data_map_size, pz_curr_file);
1332 # ifndef SEPARATE_FIX_PROC
1333 process_chain_head = NOPROCESS;
1335 /* For every fix in our fix list, ... */
1336 for (; todo_ct > 0; p_fixd++, todo_ct--)
1338 if (! fix_applies (p_fixd))
1339 continue;
1341 if (VLEVEL( VERB_APPLIES ))
1342 fprintf (stderr, "Applying %-24s to %s\n",
1343 p_fixd->fix_name, pz_curr_file);
1345 if (p_fixd->fd_flags & FD_REPLACEMENT)
1347 write_replacement (p_fixd);
1348 UNLOAD_DATA();
1349 return;
1352 /* IF we do not have a read pointer,
1353 THEN this is the first fix for the current file.
1354 Open the source file. That will be used as stdin for
1355 the first fix. Any subsequent fixes will use the
1356 stdout descriptor of the previous fix for its stdin. */
1358 if (read_fd == -1)
1360 read_fd = open (pz_curr_file, O_RDONLY);
1361 if (read_fd < 0)
1363 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1364 xstrerror (errno), pz_curr_file);
1365 exit (EXIT_FAILURE);
1368 /* Ensure we do not get duplicate output */
1370 fflush (stdout);
1373 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1374 num_children++;
1377 /* IF we have a read-back file descriptor,
1378 THEN check for changes and write output if changed. */
1380 if (read_fd >= 0)
1382 test_for_changes (read_fd);
1383 #ifdef DO_STATS
1384 apply_ct += num_children;
1385 #endif
1386 /* Wait for child processes created by chain_open()
1387 to avoid leaving zombies. */
1388 do {
1389 wait ((int *) NULL);
1390 } while (--num_children > 0);
1393 # else /* is SEPARATE_FIX_PROC */
1395 for (; todo_ct > 0; p_fixd++, todo_ct--)
1397 if (! fix_applies (p_fixd))
1398 continue;
1400 if (VLEVEL( VERB_APPLIES ))
1401 fprintf (stderr, "Applying %-24s to %s\n",
1402 p_fixd->fix_name, pz_curr_file);
1404 if (p_fixd->fd_flags & FD_REPLACEMENT)
1406 write_replacement (p_fixd);
1407 UNLOAD_DATA();
1408 return;
1410 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1411 pz_file_source = pz_temp_file;
1414 read_fd = open (pz_temp_file, O_RDONLY);
1415 if (read_fd < 0)
1417 if (errno != ENOENT)
1418 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1419 errno, xstrerror (errno), pz_temp_file);
1421 else
1423 test_for_changes (read_fd);
1424 /* Unlinking a file while it is still open is a Bad Idea on
1425 DOS/Windows. */
1426 close (read_fd);
1427 unlink (pz_temp_file);
1430 # endif
1431 UNLOAD_DATA();