2005-03-06 James A. Morrison <phython@gcc.gnu.org>
[official-gcc.git] / fixincludes / fixincl.c
blob8bd43dcb748f5e58c32da3e3cbd5c1a1a8d42bbc
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, 2004 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 #include <sys/stat.h>
28 #if defined( HAVE_MMAP_FILE )
29 #include <sys/mman.h>
30 #define BAD_ADDR ((void*)-1)
31 #endif
33 #ifndef SEPARATE_FIX_PROC
34 #include "server.h"
35 #endif
37 /* The contents of this string are not very important. It is mostly
38 just used as part of the "I am alive and working" test. */
40 static const char program_id[] = "fixincl version 1.1";
42 /* This format will be used at the start of every generated file */
44 static const char z_std_preamble[] =
45 "/* DO NOT EDIT THIS FILE.\n\n\
46 It has been auto-edited by fixincludes from:\n\n\
47 \t\"%s/%s\"\n\n\
48 This had to be done to correct non-standard usages in the\n\
49 original, manufacturer supplied header file. */\n\n";
51 int find_base_len = 0;
53 typedef enum {
54 VERB_SILENT = 0,
55 VERB_FIXES,
56 VERB_APPLIES,
57 VERB_PROGRESS,
58 VERB_TESTS,
59 VERB_EVERYTHING
60 } te_verbose;
62 te_verbose verbose_level = VERB_PROGRESS;
63 int have_tty = 0;
65 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
66 #define NOT_SILENT VLEVEL(VERB_FIXES)
68 pid_t process_chain_head = (pid_t) -1;
70 char* pz_curr_file; /* name of the current file under test/fix */
71 char* pz_curr_data; /* original contents of that file */
72 char* pz_temp_file; /* for DOS, a place to stash the temporary
73 fixed data between system(3) calls */
74 t_bool curr_data_mapped;
75 int data_map_fd;
76 size_t data_map_size;
77 size_t ttl_data_size = 0;
79 #ifdef DO_STATS
80 int process_ct = 0;
81 int apply_ct = 0;
82 int fixed_ct = 0;
83 int altered_ct = 0;
84 #endif /* DO_STATS */
86 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
87 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
88 regex_t incl_quote_re;
90 static void do_version (void) ATTRIBUTE_NORETURN;
91 char *load_file (const char *);
92 void run_compiles (void);
93 void initialize (int argc, char** argv);
94 void process (void);
96 /* External Source Code */
98 #include "fixincl.x"
100 /* * * * * * * * * * * * * * * * * * *
102 * MAIN ROUTINE
104 extern int main (int, char **);
106 main (int argc, char** argv)
108 char *file_name_buf;
110 initialize ( argc, argv );
112 have_tty = isatty (fileno (stderr));
114 /* Before anything else, ensure we can allocate our file name buffer. */
115 file_name_buf = load_file_data (stdin);
117 /* Because of the way server shells work, you have to keep stdin, out
118 and err open so that the proper input file does not get closed
119 by accident */
121 freopen ("/dev/null", "r", stdin);
123 if (file_name_buf == (char *) NULL)
125 fputs ("No file names listed for fixing\n", stderr);
126 exit (EXIT_FAILURE);
129 for (;;)
131 char* pz_end;
133 /* skip to start of name, past any "./" prefixes */
135 while (ISSPACE (*file_name_buf)) file_name_buf++;
136 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
137 file_name_buf += 2;
139 /* Check for end of list */
141 if (*file_name_buf == NUL)
142 break;
144 /* Set global file name pointer and find end of name */
146 pz_curr_file = file_name_buf;
147 pz_end = strchr( pz_curr_file, '\n' );
148 if (pz_end == (char*)NULL)
149 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
150 else
151 file_name_buf = pz_end + 1;
153 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
155 /* IF no name is found (blank line) or comment marker, skip line */
157 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
158 continue;
159 *pz_end = NUL;
161 process ();
162 } /* for (;;) */
164 #ifdef DO_STATS
165 if (VLEVEL( VERB_PROGRESS )) {
166 tSCC zFmt[] =
168 Processed %5d files containing %d bytes \n\
169 Applying %5d fixes to %d files\n\
170 Altering %5d of them\n";
172 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
173 fixed_ct, altered_ct);
175 #endif /* DO_STATS */
177 # ifdef SEPARATE_FIX_PROC
178 unlink( pz_temp_file );
179 # endif
180 exit (EXIT_SUCCESS);
184 static void
185 do_version (void)
187 static const char zFmt[] = "echo '%s'";
188 char zBuf[ 1024 ];
190 /* The 'version' option is really used to test that:
191 1. The program loads correctly (no missing libraries)
192 2. that we can compile all the regular expressions.
193 3. we can correctly run our server shell process
195 run_compiles ();
196 sprintf (zBuf, zFmt, program_id);
197 #ifndef SEPARATE_FIX_PROC
198 puts (zBuf + 5);
199 exit (strcmp (run_shell (zBuf), program_id));
200 #else
201 exit (system (zBuf));
202 #endif
205 /* * * * * * * * * * * * */
207 void
208 initialize ( int argc, char** argv )
210 xmalloc_set_program_name (argv[0]);
212 switch (argc)
214 case 1:
215 break;
217 case 2:
218 if (strcmp (argv[1], "-v") == 0)
219 do_version ();
220 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
222 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
223 errno, xstrerror (errno), argv[1] );
224 exit (EXIT_FAILURE);
226 break;
228 default:
229 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
230 exit (EXIT_FAILURE);
233 #ifdef SIGCHLD
234 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
235 receive the signal. A different setting is inheritable */
236 signal (SIGCHLD, SIG_DFL);
237 #endif
239 initialize_opts ();
241 if (ISDIGIT ( *pz_verbose ))
242 verbose_level = (te_verbose)atoi( pz_verbose );
243 else
244 switch (*pz_verbose) {
245 case 's':
246 case 'S':
247 verbose_level = VERB_SILENT; break;
249 case 'f':
250 case 'F':
251 verbose_level = VERB_FIXES; break;
253 case 'a':
254 case 'A':
255 verbose_level = VERB_APPLIES; break;
257 default:
258 case 'p':
259 case 'P':
260 verbose_level = VERB_PROGRESS; break;
262 case 't':
263 case 'T':
264 verbose_level = VERB_TESTS; break;
266 case 'e':
267 case 'E':
268 verbose_level = VERB_EVERYTHING; break;
270 if (verbose_level >= VERB_EVERYTHING) {
271 verbose_level = VERB_EVERYTHING;
272 fputs ("fixinc verbosity: EVERYTHING\n", stderr);
274 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
275 pz_find_base += 2;
276 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
277 find_base_len = strlen( pz_find_base );
279 /* Compile all the regular expressions now.
280 That way, it is done only once for the whole run.
282 run_compiles ();
284 # ifdef SEPARATE_FIX_PROC
285 /* NULL as the first argument to `tempnam' causes it to DTRT
286 wrt the temporary directory where the file will be created. */
287 pz_temp_file = tempnam( NULL, "fxinc" );
288 # endif
290 signal (SIGQUIT, SIG_IGN);
291 signal (SIGIOT, SIG_IGN);
292 signal (SIGPIPE, SIG_IGN);
293 signal (SIGALRM, SIG_IGN);
294 signal (SIGTERM, SIG_IGN);
297 /* * * * * * * * * * * * *
299 load_file loads all the contents of a file into malloc-ed memory.
300 Its argument is the name of the file to read in; the returned
301 result is the NUL terminated contents of the file. The file
302 is presumed to be an ASCII text file containing no NULs. */
303 char *
304 load_file ( const char* fname )
306 struct stat stbf;
307 char* res;
309 if (stat (fname, &stbf) != 0)
311 if (NOT_SILENT)
312 fprintf (stderr, "error %d (%s) stat-ing %s\n",
313 errno, xstrerror (errno), fname );
314 return (char *) NULL;
316 if (stbf.st_size == 0)
317 return (char*)NULL;
319 /* Make the data map size one larger than the file size for documentation
320 purposes. Truth is that there will be a following NUL character if
321 the file size is not a multiple of the page size. If it is a multiple,
322 then this adjustment sometimes fails anyway. */
323 data_map_size = stbf.st_size+1;
324 data_map_fd = open (fname, O_RDONLY);
325 ttl_data_size += data_map_size-1;
327 if (data_map_fd < 0)
329 if (NOT_SILENT)
330 fprintf (stderr, "error %d (%s) opening %s for read\n",
331 errno, xstrerror (errno), fname);
332 return (char*)NULL;
335 #ifdef HAVE_MMAP_FILE
336 curr_data_mapped = BOOL_TRUE;
338 /* IF the file size is a multiple of the page size,
339 THEN sometimes you will seg fault trying to access a trailing byte */
340 if ((stbf.st_size & (getpagesize()-1)) == 0)
341 res = (char*)BAD_ADDR;
342 else
343 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
344 MAP_PRIVATE, data_map_fd, 0);
345 if (res == (char*)BAD_ADDR)
346 #endif
348 FILE* fp = fdopen (data_map_fd, "r");
349 curr_data_mapped = BOOL_FALSE;
350 res = load_file_data (fp);
351 fclose (fp);
354 return res;
357 static int
358 machine_matches( tFixDesc* p_fixd )
360 # ifndef SEPARATE_FIX_PROC
361 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
362 tSCC esac_fmt[] =
363 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
364 tSCC skip[] = "skip"; /* 4 bytes */
365 tSCC run[] = "run"; /* 3 bytes */
366 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
368 const char **papz_machs = p_fixd->papz_machs;
369 char *pz;
370 const char *pz_sep = "";
371 tCC *pz_if_true;
372 tCC *pz_if_false;
373 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
375 /* Start the case statement */
377 sprintf (cmd_buf, case_fmt, pz_machine);
378 pz = cmd_buf + strlen (cmd_buf);
380 /* Determine if a match means to apply the fix or not apply it */
382 if (p_fixd->fd_flags & FD_MACH_IFNOT)
384 pz_if_true = skip;
385 pz_if_false = run;
387 else
389 pz_if_true = run;
390 pz_if_false = skip;
393 /* Emit all the machine names. If there are more than one,
394 then we will insert " | \\\n" between the names */
396 for (;;)
398 const char* pz_mach = *(papz_machs++);
400 if (pz_mach == (const char*) NULL)
401 break;
402 sprintf (pz, "%s%s", pz_sep, pz_mach);
403 pz += strlen (pz);
404 pz_sep = " | \\\n";
407 /* Now emit the match and not-match actions and the esac */
409 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
411 /* Run the script.
412 The result will start either with 's' or 'r'. */
415 int skip;
416 pz = run_shell (cmd_buf);
417 skip = (*pz == 's');
418 free ( (void*)pz );
419 if (skip)
421 p_fixd->fd_flags |= FD_SKIP_TEST;
422 return BOOL_FALSE;
426 return BOOL_TRUE;
427 # else /* is SEPARATE_FIX_PROC */
428 const char **papz_machs = p_fixd->papz_machs;
429 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
430 for (;;)
432 const char* pz_mach = *(papz_machs++);
434 if (pz_mach == (const char*) NULL)
435 break;
436 if (strstr (pz_mach, "dos") != NULL && !invert)
437 return BOOL_TRUE;
440 p_fixd->fd_flags |= FD_SKIP_TEST;
441 return BOOL_FALSE;
442 # endif
445 /* * * * * * * * * * * * *
447 run_compiles run all the regexp compiles for all the fixes once.
449 void
450 run_compiles (void)
452 tFixDesc *p_fixd = fixDescList;
453 int fix_ct = FIX_COUNT;
454 regex_t *p_re = xcalloc (REGEX_COUNT, sizeof (regex_t));
456 /* Make sure compile_re does not stumble across invalid data */
458 memset (&incl_quote_re, '\0', sizeof (regex_t));
460 compile_re (incl_quote_pat, &incl_quote_re, 1,
461 "quoted include", "run_compiles");
463 /* Allow machine name tests to be ignored (testing, mainly) */
465 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
466 pz_machine = (char*)NULL;
468 /* FOR every fixup, ... */
471 tTestDesc *p_test = p_fixd->p_test_desc;
472 int test_ct = p_fixd->test_ct;
474 /* IF the machine type pointer is not NULL (we are not in test mode)
475 AND this test is for or not done on particular machines
476 THEN ... */
478 if ( (pz_machine != NULL)
479 && (p_fixd->papz_machs != (const char**) NULL)
480 && ! machine_matches (p_fixd) )
481 continue;
483 /* FOR every test for the fixup, ... */
485 while (--test_ct >= 0)
487 switch (p_test->type)
489 case TT_EGREP:
490 case TT_NEGREP:
491 p_test->p_test_regex = p_re++;
492 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
493 "select test", p_fixd->fix_name);
494 default: break;
496 p_test++;
499 while (p_fixd++, --fix_ct > 0);
503 /* * * * * * * * * * * * *
505 create_file Create the output modified file.
506 Input: the name of the file to create
507 Returns: a file pointer to the new, open file */
509 #if defined(S_IRUSR) && defined(S_IWUSR) && \
510 defined(S_IRGRP) && defined(S_IROTH)
512 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
513 #else
514 # define S_IRALL 0644
515 #endif
517 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
518 defined(S_IROTH) && defined(S_IXOTH)
520 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
521 #else
522 # define S_DIRALL 0755
523 #endif
526 static FILE *
527 create_file (void)
529 int fd;
530 FILE *pf;
531 char fname[MAXPATHLEN];
533 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
535 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
537 /* We may need to create the directories needed... */
538 if ((fd < 0) && (errno == ENOENT))
540 char *pz_dir = strchr (fname + 1, '/');
541 struct stat stbf;
543 while (pz_dir != (char *) NULL)
545 *pz_dir = NUL;
546 if (stat (fname, &stbf) < 0)
548 #ifdef _WIN32
549 mkdir (fname);
550 #else
551 mkdir (fname, S_IFDIR | S_DIRALL);
552 #endif
555 *pz_dir = '/';
556 pz_dir = strchr (pz_dir + 1, '/');
559 /* Now, lets try the open again... */
560 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
562 if (fd < 0)
564 fprintf (stderr, "Error %d (%s) creating %s\n",
565 errno, xstrerror (errno), fname);
566 exit (EXIT_FAILURE);
568 if (NOT_SILENT)
569 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
570 pf = fdopen (fd, "w");
573 * IF pz_machine is NULL, then we are in some sort of test mode.
574 * Do not insert the current directory name. Use a constant string.
576 fprintf (pf, z_std_preamble,
577 (pz_machine == NULL)
578 ? "fixinc/tests/inc"
579 : pz_input_dir,
580 pz_curr_file);
582 return pf;
586 /* * * * * * * * * * * * *
588 test_test make sure a shell-style test expression passes.
589 Input: a pointer to the descriptor of the test to run and
590 the name of the file that we might want to fix
591 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
592 shell script we run. */
593 #ifndef SEPARATE_FIX_PROC
594 static int
595 test_test (tTestDesc* p_test, char* pz_test_file)
597 tSCC cmd_fmt[] =
598 "file=%s\n\
599 if ( test %s ) > /dev/null 2>&1\n\
600 then echo TRUE\n\
601 else echo FALSE\n\
602 fi";
604 char *pz_res;
605 int res;
607 static char cmd_buf[4096];
609 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
610 pz_res = run_shell (cmd_buf);
612 switch (*pz_res) {
613 case 'T':
614 res = APPLY_FIX;
615 break;
617 case 'F':
618 res = SKIP_FIX;
619 break;
621 default:
622 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
623 pz_res, cmd_buf );
624 res = SKIP_FIX;
627 free ((void *) pz_res);
628 return res;
630 #else
632 * IF we are in MS-DOS land, then whatever shell-type test is required
633 * will, by definition, fail
635 #define test_test(t,tf) SKIP_FIX
636 #endif
638 /* * * * * * * * * * * * *
640 egrep_test make sure an egrep expression is found in the file text.
641 Input: a pointer to the descriptor of the test to run and
642 the pointer to the contents of the file under suspicion
643 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
645 The caller may choose to reverse meaning if the sense of the test
646 is inverted. */
648 static int
649 egrep_test (char* pz_data, tTestDesc* p_test)
651 #ifdef DEBUG
652 if (p_test->p_test_regex == 0)
653 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
654 p_test->pz_test_text);
655 #endif
656 if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
657 return APPLY_FIX;
658 return SKIP_FIX;
662 /* * * * * * * * * * * * *
664 quoted_file_exists Make sure that a file exists before we emit
665 the file name. If we emit the name, our invoking shell will try
666 to copy a non-existing file into the destination directory. */
668 static int
669 quoted_file_exists (const char* pz_src_path,
670 const char* pz_file_path,
671 const char* pz_file)
673 char z[ MAXPATHLEN ];
674 char* pz;
675 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
676 pz = z + strlen ( z );
678 for (;;) {
679 char ch = *pz_file++;
680 if (! ISGRAPH( ch ))
681 return 0;
682 if (ch == '"')
683 break;
684 *pz++ = ch;
686 *pz = '\0';
688 struct stat s;
689 if (stat (z, &s) != 0)
690 return 0;
691 return S_ISREG( s.st_mode );
696 /* * * * * * * * * * * * *
698 extract_quoted_files
700 The syntax, `#include "file.h"' specifies that the compiler is to
701 search the local directory of the current file before the include
702 list. Consequently, if we have modified a header and stored it in
703 another directory, any files that are included by that modified
704 file in that fashion must also be copied into this new directory.
705 This routine finds those flavors of #include and for each one found
706 emits a triple of:
708 1. source directory of the original file
709 2. the relative path file name of the #includ-ed file
710 3. the full destination path for this file
712 Input: the text of the file, the file name and a pointer to the
713 match list where the match information was stored.
714 Result: internally nothing. The results are written to stdout
715 for interpretation by the invoking shell */
718 static void
719 extract_quoted_files (char* pz_data,
720 const char* pz_fixed_file,
721 regmatch_t* p_re_match)
723 char *pz_dir_end = strrchr (pz_fixed_file, '/');
724 char *pz_incl_quot = pz_data;
726 if (VLEVEL( VERB_APPLIES ))
727 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
729 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
730 If there is none, then it is in our current directory, ".". */
732 if (pz_dir_end == (char *) NULL)
733 pz_fixed_file = ".";
734 else
735 *pz_dir_end = '\0';
737 for (;;)
739 pz_incl_quot += p_re_match->rm_so;
741 /* Skip forward to the included file name */
742 while (*pz_incl_quot != '"')
743 pz_incl_quot++;
745 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
747 /* Print the source directory and the subdirectory
748 of the file in question. */
749 printf ("%s %s/", pz_src_dir, pz_fixed_file);
750 pz_dir_end = pz_incl_quot;
752 /* Append to the directory the relative path of the desired file */
753 while (*pz_incl_quot != '"')
754 putc (*pz_incl_quot++, stdout);
756 /* Now print the destination directory appended with the
757 relative path of the desired file */
758 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
759 while (*pz_dir_end != '"')
760 putc (*pz_dir_end++, stdout);
762 /* End of entry */
763 putc ('\n', stdout);
766 /* Find the next entry */
767 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
768 break;
773 /* * * * * * * * * * * * *
775 Somebody wrote a *_fix subroutine that we must call.
777 #ifndef SEPARATE_FIX_PROC
778 static int
779 internal_fix (int read_fd, tFixDesc* p_fixd)
781 int fd[2];
783 if (pipe( fd ) != 0)
785 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
786 exit (EXIT_FAILURE);
789 for (;;)
791 pid_t childid = fork();
793 switch (childid)
795 case -1:
796 break;
798 case 0:
799 close (fd[0]);
800 goto do_child_task;
802 default:
804 * Parent process
806 close (read_fd);
807 close (fd[1]);
808 return fd[0];
812 * Parent in error
814 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
815 p_fixd->fix_name);
817 static int failCt = 0;
818 if ((errno != EAGAIN) || (++failCt > 10))
819 exit (EXIT_FAILURE);
820 sleep (1);
822 } do_child_task:;
825 * Close our current stdin and stdout
827 close (STDIN_FILENO);
828 close (STDOUT_FILENO);
829 UNLOAD_DATA();
832 * Make the fd passed in the stdin, and the write end of
833 * the new pipe become the stdout.
835 dup2 (fd[1], STDOUT_FILENO);
836 dup2 (read_fd, STDIN_FILENO);
838 apply_fix (p_fixd, pz_curr_file);
839 exit (0);
841 #endif /* !SEPARATE_FIX_PROC */
844 #ifdef SEPARATE_FIX_PROC
845 static void
846 fix_with_system (tFixDesc* p_fixd,
847 tCC* pz_fix_file,
848 tCC* pz_file_source,
849 tCC* pz_temp_file)
851 char* pz_cmd;
852 char* pz_scan;
853 size_t argsize;
855 if (p_fixd->fd_flags & FD_SUBROUTINE)
857 static const char z_applyfix_prog[] =
858 "/../fixincludes/applyfix" EXE_EXT;
860 struct stat buf;
861 argsize = 32
862 + strlen (pz_orig_dir)
863 + sizeof (z_applyfix_prog)
864 + strlen (pz_fix_file)
865 + strlen (pz_file_source)
866 + strlen (pz_temp_file);
868 /* Allocate something sure to be big enough for our purposes */
869 pz_cmd = xmalloc (argsize);
870 strcpy (pz_cmd, pz_orig_dir);
871 pz_scan = pz_cmd + strlen (pz_orig_dir);
873 strcpy (pz_scan, z_applyfix_prog);
875 /* IF we can't find the "applyfix" executable file at the first guess,
876 try one level higher up */
877 if (stat (pz_cmd, &buf) == -1)
879 strcpy (pz_scan, "/..");
880 strcpy (pz_scan+3, z_applyfix_prog);
883 pz_scan += strlen (pz_scan);
886 * Now add the fix number and file names that may be needed
888 sprintf (pz_scan, " %ld '%s' '%s' '%s'", p_fixd - fixDescList,
889 pz_fix_file, pz_file_source, pz_temp_file);
891 else /* NOT an "internal" fix: */
893 size_t parg_size;
894 #ifdef __MSDOS__
895 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
896 dst is a temporary file anyway, so we know there's no other
897 file by that name; and DOS's system(3) doesn't mind to
898 clobber existing file in redirection. Besides, with DOS 8+3
899 limited file namespace, we can easily lose if dst already has
900 an extension that is 3 or more characters long.
902 I do not think the 8+3 issue is relevant because all the files
903 we operate on are named "*.h", making 8+2 adequate. Anyway,
904 the following bizarre use of 'cat' only works on DOS boxes.
905 It causes the file to be dropped into a temporary file for
906 'cat' to read (pipes do not work on DOS). */
907 tSCC z_cmd_fmt[] = " '%s' | cat > '%s'";
908 #else
909 /* Don't use positional formatting arguments because some lame-o
910 implementations cannot cope :-(. */
911 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
912 #endif
913 tCC** ppArgs = p_fixd->patch_args;
915 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
916 + strlen( pz_file_source );
917 parg_size = argsize;
921 * Compute the size of the command line. Add lotsa extra space
922 * because some of the args to sed use lotsa single quotes.
923 * (This requires three extra bytes per quote. Here we allow
924 * for up to 8 single quotes for each argument, including the
925 * command name "sed" itself. Nobody will *ever* need more. :)
927 for (;;)
929 tCC* p_arg = *(ppArgs++);
930 if (p_arg == NULL)
931 break;
932 argsize += 24 + strlen( p_arg );
935 /* Estimated buffer size we will need. */
936 pz_scan = pz_cmd = xmalloc (argsize);
937 /* How much of it do we allot to the program name and its
938 arguments. */
939 parg_size = argsize - parg_size;
941 ppArgs = p_fixd->patch_args;
944 * Copy the program name, unquoted
947 tCC* pArg = *(ppArgs++);
948 for (;;)
950 char ch = *(pArg++);
951 if (ch == NUL)
952 break;
953 *(pz_scan++) = ch;
958 * Copy the program arguments, quoted
960 for (;;)
962 tCC* pArg = *(ppArgs++);
963 char* pz_scan_save;
964 if (pArg == NULL)
965 break;
966 *(pz_scan++) = ' ';
967 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
968 parg_size - (pz_scan - pz_cmd) );
970 * Make sure we don't overflow the buffer due to sloppy
971 * size estimation.
973 while (pz_scan == (char*)NULL)
975 size_t already_filled = pz_scan_save - pz_cmd;
976 pz_cmd = xrealloc (pz_cmd, argsize += 100);
977 pz_scan_save = pz_scan = pz_cmd + already_filled;
978 parg_size += 100;
979 pz_scan = make_raw_shell_str( pz_scan, pArg,
980 parg_size - (pz_scan - pz_cmd) );
985 * add the file machinations.
987 #ifdef __MSDOS__
988 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
989 #else
990 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
991 pz_temp_file, pz_temp_file, pz_temp_file);
992 #endif
994 system( pz_cmd );
995 free( (void*)pz_cmd );
998 /* * * * * * * * * * * * *
1000 This loop should only cycle for 1/2 of one loop.
1001 "chain_open" starts a process that uses "read_fd" as
1002 its stdin and returns the new fd this process will use
1003 for stdout. */
1005 #else /* is *NOT* SEPARATE_FIX_PROC */
1006 static int
1007 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
1009 tCC* pz_cmd_save;
1010 char* pz_cmd;
1012 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1013 return internal_fix (read_fd, p_fixd);
1015 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1017 pz_cmd = NULL;
1018 pz_cmd_save = NULL;
1020 else
1022 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1023 pz_cmd = xmalloc (strlen (p_fixd->patch_args[2])
1024 + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
1025 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1026 pz_cmd_save = p_fixd->patch_args[2];
1027 p_fixd->patch_args[2] = pz_cmd;
1030 /* Start a fix process, handing off the previous read fd for its
1031 stdin and getting a new fd that reads from the fix process' stdout.
1032 We normally will not loop, but we will up to 10 times if we keep
1033 getting "EAGAIN" errors.
1036 for (;;)
1038 static int failCt = 0;
1039 int fd;
1041 fd = chain_open (read_fd,
1042 (tCC **) p_fixd->patch_args,
1043 (process_chain_head == -1)
1044 ? &process_chain_head : (pid_t *) NULL);
1046 if (fd != -1)
1048 read_fd = fd;
1049 break;
1052 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1053 p_fixd->fix_name);
1055 if ((errno != EAGAIN) || (++failCt > 10))
1056 exit (EXIT_FAILURE);
1057 sleep (1);
1060 /* IF we allocated a shell script command,
1061 THEN free it and restore the command format to the fix description */
1062 if (pz_cmd != (char*)NULL)
1064 free ((void*)pz_cmd);
1065 p_fixd->patch_args[2] = pz_cmd_save;
1068 return read_fd;
1070 #endif
1073 /* * * * * * * * * * * * *
1075 Process the potential fixes for a particular include file.
1076 Input: the original text of the file and the file's name
1077 Result: none. A new file may or may not be created. */
1079 static t_bool
1080 fix_applies (tFixDesc* p_fixd)
1082 const char *pz_fname = pz_curr_file;
1083 const char *pz_scan = p_fixd->file_list;
1084 int test_ct;
1085 tTestDesc *p_test;
1087 # ifdef SEPARATE_FIX_PROC
1089 * There is only one fix that uses a shell script as of this writing.
1090 * I hope to nuke it anyway, it does not apply to DOS and it would
1091 * be painful to implement. Therefore, no "shell" fixes for DOS.
1093 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1094 return BOOL_FALSE;
1095 # else
1096 if (p_fixd->fd_flags & FD_SKIP_TEST)
1097 return BOOL_FALSE;
1098 # endif
1100 /* IF there is a file name restriction,
1101 THEN ensure the current file name matches one in the pattern */
1103 if (pz_scan != (char *) NULL)
1105 size_t name_len;
1107 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1108 pz_fname += 2;
1109 name_len = strlen (pz_fname);
1111 for (;;)
1113 pz_scan = strstr (pz_scan + 1, pz_fname);
1114 /* IF we can't match the string at all,
1115 THEN bail */
1116 if (pz_scan == (char *) NULL)
1117 return BOOL_FALSE;
1119 /* IF the match is surrounded by the '|' markers,
1120 THEN we found a full match -- time to run the tests */
1122 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1123 break;
1127 /* FOR each test, see if it fails.
1128 IF it does fail, then we go on to the next test */
1130 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1131 test_ct-- > 0;
1132 p_test++)
1134 switch (p_test->type)
1136 case TT_TEST:
1137 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1138 #ifdef DEBUG
1139 if (VLEVEL( VERB_EVERYTHING ))
1140 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1141 pz_fname, p_fixd->test_ct - test_ct);
1142 #endif
1143 return BOOL_FALSE;
1145 break;
1147 case TT_EGREP:
1148 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1149 #ifdef DEBUG
1150 if (VLEVEL( VERB_EVERYTHING ))
1151 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1152 pz_fname, p_fixd->test_ct - test_ct);
1153 #endif
1154 return BOOL_FALSE;
1156 break;
1158 case TT_NEGREP:
1159 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1160 #ifdef DEBUG
1161 if (VLEVEL( VERB_EVERYTHING ))
1162 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1163 pz_fname, p_fixd->test_ct - test_ct);
1164 #endif
1165 /* Negated sense */
1166 return BOOL_FALSE;
1168 break;
1170 case TT_FUNCTION:
1171 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1172 != APPLY_FIX) {
1173 #ifdef DEBUG
1174 if (VLEVEL( VERB_EVERYTHING ))
1175 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1176 pz_fname, p_fixd->test_ct - test_ct);
1177 #endif
1178 return BOOL_FALSE;
1180 break;
1184 return BOOL_TRUE;
1188 /* * * * * * * * * * * * *
1190 Write out a replacement file */
1192 static void
1193 write_replacement (tFixDesc* p_fixd)
1195 const char* pz_text = p_fixd->patch_args[0];
1197 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1198 return;
1201 FILE* out_fp = create_file ();
1202 fputs (pz_text, out_fp);
1203 fclose (out_fp);
1208 /* * * * * * * * * * * * *
1210 We have work to do. Read back in the output
1211 of the filtering chain. Compare each byte as we read it with
1212 the contents of the original file. As soon as we find any
1213 difference, we will create the output file, write out all
1214 the matched text and then copy any remaining data from the
1215 output of the filter chain.
1217 static void
1218 test_for_changes (int read_fd)
1220 FILE *in_fp = fdopen (read_fd, "r");
1221 FILE *out_fp = (FILE *) NULL;
1222 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1224 #ifdef DO_STATS
1225 fixed_ct++;
1226 #endif
1227 for (;;)
1229 int ch;
1231 ch = getc (in_fp);
1232 if (ch == EOF)
1233 break;
1234 ch &= 0xFF; /* all bytes are 8 bits */
1236 /* IF we are emitting the output
1237 THEN emit this character, too.
1239 if (out_fp != (FILE *) NULL)
1240 putc (ch, out_fp);
1242 /* ELSE if this character does not match the original,
1243 THEN now is the time to start the output.
1245 else if (ch != *pz_cmp)
1247 out_fp = create_file ();
1249 #ifdef DO_STATS
1250 altered_ct++;
1251 #endif
1252 /* IF there are matched data, write the matched part now. */
1253 if ((char*)pz_cmp != pz_curr_data)
1254 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
1255 1, out_fp);
1257 /* Emit the current unmatching character */
1258 putc (ch, out_fp);
1260 else
1261 /* ELSE the character matches. Advance the compare ptr */
1262 pz_cmp++;
1265 /* IF we created the output file, ... */
1266 if (out_fp != (FILE *) NULL)
1268 regmatch_t match;
1270 /* Close the file and see if we have to worry about
1271 `#include "file.h"' constructs. */
1272 fclose (out_fp);
1273 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1274 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1277 fclose (in_fp);
1278 close (read_fd); /* probably redundant, but I'm paranoid */
1282 /* * * * * * * * * * * * *
1284 Process the potential fixes for a particular include file.
1285 Input: the original text of the file and the file's name
1286 Result: none. A new file may or may not be created. */
1288 void
1289 process (void)
1291 tFixDesc *p_fixd = fixDescList;
1292 int todo_ct = FIX_COUNT;
1293 int read_fd = -1;
1294 # ifndef SEPARATE_FIX_PROC
1295 int num_children = 0;
1296 # else /* is SEPARATE_FIX_PROC */
1297 char* pz_file_source = pz_curr_file;
1298 # endif
1300 if (access (pz_curr_file, R_OK) != 0)
1302 int erno = errno;
1303 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1304 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1305 erno, xstrerror (erno));
1306 return;
1309 pz_curr_data = load_file (pz_curr_file);
1310 if (pz_curr_data == (char *) NULL)
1311 return;
1313 #ifdef DO_STATS
1314 process_ct++;
1315 #endif
1316 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1317 fprintf (stderr, "%6lu %-50s \r",
1318 (unsigned long) data_map_size, pz_curr_file);
1320 # ifndef SEPARATE_FIX_PROC
1321 process_chain_head = NOPROCESS;
1323 /* For every fix in our fix list, ... */
1324 for (; todo_ct > 0; p_fixd++, todo_ct--)
1326 if (! fix_applies (p_fixd))
1327 continue;
1329 if (VLEVEL( VERB_APPLIES ))
1330 fprintf (stderr, "Applying %-24s to %s\n",
1331 p_fixd->fix_name, pz_curr_file);
1333 if (p_fixd->fd_flags & FD_REPLACEMENT)
1335 write_replacement (p_fixd);
1336 UNLOAD_DATA();
1337 return;
1340 /* IF we do not have a read pointer,
1341 THEN this is the first fix for the current file.
1342 Open the source file. That will be used as stdin for
1343 the first fix. Any subsequent fixes will use the
1344 stdout descriptor of the previous fix for its stdin. */
1346 if (read_fd == -1)
1348 read_fd = open (pz_curr_file, O_RDONLY);
1349 if (read_fd < 0)
1351 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1352 xstrerror (errno), pz_curr_file);
1353 exit (EXIT_FAILURE);
1356 /* Ensure we do not get duplicate output */
1358 fflush (stdout);
1361 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1362 num_children++;
1365 /* IF we have a read-back file descriptor,
1366 THEN check for changes and write output if changed. */
1368 if (read_fd >= 0)
1370 test_for_changes (read_fd);
1371 #ifdef DO_STATS
1372 apply_ct += num_children;
1373 #endif
1374 /* Wait for child processes created by chain_open()
1375 to avoid leaving zombies. */
1376 do {
1377 wait ((int *) NULL);
1378 } while (--num_children > 0);
1381 # else /* is SEPARATE_FIX_PROC */
1383 for (; todo_ct > 0; p_fixd++, todo_ct--)
1385 if (! fix_applies (p_fixd))
1386 continue;
1388 if (VLEVEL( VERB_APPLIES ))
1389 fprintf (stderr, "Applying %-24s to %s\n",
1390 p_fixd->fix_name, pz_curr_file);
1392 if (p_fixd->fd_flags & FD_REPLACEMENT)
1394 write_replacement (p_fixd);
1395 UNLOAD_DATA();
1396 return;
1398 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1399 pz_file_source = pz_temp_file;
1402 read_fd = open (pz_temp_file, O_RDONLY);
1403 if (read_fd < 0)
1405 if (errno != ENOENT)
1406 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1407 errno, xstrerror (errno), pz_temp_file);
1409 else
1411 test_for_changes (read_fd);
1412 /* Unlinking a file while it is still open is a Bad Idea on
1413 DOS/Windows. */
1414 close (read_fd);
1415 unlink (pz_temp_file);
1418 # endif
1419 UNLOAD_DATA();