* rtl.h (rtunion_def): Constify member `rtstr'.
[official-gcc.git] / gcc / fixinc / fixincl.c
blobfb5f68b22ea55a3ade6487eae8d3deca0c48ca97
2 /* Install modified versions of certain ANSI-incompatible system header
3 files which are fixed to work correctly with ANSI C and placed in a
4 directory that GNU C will search.
6 Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
8 This file is part of GNU CC.
10 GNU CC is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
15 GNU CC is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with GNU CC; see the file COPYING. If not, write to
22 the Free Software Foundation, 59 Temple Place - Suite 330,
23 Boston, MA 02111-1307, USA. */
25 #include "fixlib.h"
27 #if HAVE_MMAP
28 #include <sys/mman.h>
29 #define BAD_ADDR ((void*)-1)
30 #endif
32 #include <signal.h>
34 #include "server.h"
36 #define NO_BOGOSITY
38 /* Quality Assurance Marker :-)
40 Any file that contains this string is presumed to have
41 been carefully constructed and will not be fixed */
43 /* The contents of this string are not very important. It is mostly
44 just used as part of the "I am alive and working" test. */
46 static const char program_id[] = "fixincl version 1.1";
48 /* Test Descriptor
50 Each fix may have associated tests that determine
51 whether the fix needs to be applied or not.
52 Each test has a type (from the te_test_type enumeration);
53 associated test text; and, if the test is TT_EGREP or
54 the negated form TT_NEGREP, a pointer to the compiled
55 version of the text string.
58 typedef enum
60 TT_TEST, TT_EGREP, TT_NEGREP, TT_FUNCTION
61 } te_test_type;
63 typedef struct test_desc tTestDesc;
65 struct test_desc
67 te_test_type type;
68 const char *pz_test_text;
69 regex_t *p_test_regex;
72 typedef struct patch_desc tPatchDesc;
74 /* Fix Descriptor
76 Everything you ever wanted to know about how to apply
77 a particular fix (which files, how to qualify them,
78 how to actually make the fix, etc...)
80 NB: the FD_ defines are BIT FLAGS
83 #define FD_MACH_ONLY 0x0000
84 #define FD_MACH_IFNOT 0x0001
85 #define FD_SHELL_SCRIPT 0x0002
86 #define FD_SUBROUTINE 0x0004
87 #define FD_REPLACEMENT 0x0008
88 #define FD_SKIP_TEST 0x8000
90 typedef struct fix_desc tFixDesc;
91 struct fix_desc
93 const char* fix_name; /* Name of the fix */
94 const char* file_list; /* List of files it applies to */
95 const char** papz_machs; /* List of machine/os-es it applies to */
96 regex_t* unused;
97 int test_ct;
98 int fd_flags;
99 tTestDesc* p_test_desc;
100 const char** patch_args;
103 /* Working environment strings. Essentially, invocation 'options'. */
104 char *pz_dest_dir = NULL;
105 char *pz_src_dir = NULL;
106 char *pz_machine = NULL;
107 int find_base_len = 0;
109 typedef enum {
110 VERB_SILENT = 0,
111 VERB_FIXES,
112 VERB_APPLIES,
113 VERB_PROGRESS,
114 VERB_TESTS,
115 VERB_EVERYTHING
116 } te_verbose;
118 te_verbose verbose_level = VERB_PROGRESS;
119 int have_tty = 0;
121 #define VLEVEL(l) (verbose_level >= l)
122 #define NOT_SILENT VLEVEL(VERB_FIXES)
124 pid_t process_chain_head = (pid_t) -1;
126 char* pz_curr_file; /* name of the current file under test/fix */
127 char* pz_curr_data; /* original contents of that file */
128 t_bool curr_data_mapped;
129 int data_map_fd;
130 size_t data_map_size;
131 size_t ttl_data_size = 0;
132 #ifdef DO_STATS
133 int process_ct = 0;
134 int apply_ct = 0;
135 int fixed_ct = 0;
136 int altered_ct = 0;
137 #endif /* DO_STATS */
139 #ifdef HAVE_MMAP
140 #define UNLOAD_DATA() do { if (curr_data_mapped) { \
141 munmap ((void*)pz_curr_data, data_map_size); close (data_map_fd); } \
142 else free ((void*)pz_curr_data); } while(0)
143 #else
144 #define UNLOAD_DATA() free ((void*)pz_curr_data)
145 #endif
147 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
148 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
149 regex_t incl_quote_re;
151 void do_version ();
152 char *load_file _P_((const char *));
153 void process _P_((char *, const char *));
154 void run_compiles ();
155 void initialize ();
156 void process ();
158 /* External Source Code */
160 #include "fixincl.x"
161 #include "fixtests.c"
162 #include "fixfixes.c"
164 /* * * * * * * * * * * * * * * * * * *
166 * MAIN ROUTINE
169 main (argc, argv)
170 int argc;
171 char **argv;
173 char *file_name_buf;
175 switch (argc)
177 case 1:
178 break;
180 case 2:
181 if (strcmp (argv[1], "-v") == 0)
182 do_version ();
183 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
185 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
186 errno, xstrerror (errno), argv[1] );
187 exit (EXIT_FAILURE);
189 break;
191 default:
192 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
193 exit (EXIT_FAILURE);
196 initialize ();
198 have_tty = isatty (fileno (stderr));
200 /* Before anything else, ensure we can allocate our file name buffer. */
201 file_name_buf = load_file_data (stdin);
203 /* Because of the way server shells work, you have to keep stdin, out
204 and err open so that the proper input file does not get closed
205 by accident */
207 freopen ("/dev/null", "r", stdin);
209 if (file_name_buf == (char *) NULL)
211 fputs ("No file names listed for fixing\n", stderr);
212 exit (EXIT_FAILURE);
215 for (;;)
217 char* pz_end;
219 /* skip to start of name, past any "./" prefixes */
221 while (ISSPACE (*file_name_buf)) file_name_buf++;
222 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
223 file_name_buf += 2;
225 /* Check for end of list */
227 if (*file_name_buf == NUL)
228 break;
230 /* Set global file name pointer and find end of name */
232 pz_curr_file = file_name_buf;
233 pz_end = strchr( pz_curr_file, '\n' );
234 if (pz_end == (char*)NULL)
235 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
236 else
237 file_name_buf = pz_end + 1;
239 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
241 /* IF no name is found (blank line) or comment marker, skip line */
243 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
244 continue;
245 *pz_end = NUL;
247 #ifdef NO_BOGOSITY
248 process ();
249 #else
250 /* Prevent duplicate output by child process */
252 fflush (stdout);
253 fflush (stderr);
256 void wait_for_pid _P_(( pid_t ));
257 pid_t child = fork ();
258 if (child == NULLPROCESS)
260 process ();
261 return EXIT_SUCCESS;
264 if (child == NOPROCESS)
266 fprintf (stderr, "Error %d (%s) forking in main\n",
267 errno, xstrerror (errno));
268 exit (EXIT_FAILURE);
271 wait_for_pid( child );
273 #endif
274 } /* for (;;) */
276 #ifdef DO_STATS
277 if (VLEVEL( VERB_PROGRESS )) {
278 tSCC zFmt[] =
280 Processed %5d files containing %d bytes \n\
281 Applying %5d fixes to %d files\n\
282 Altering %5d of them\n";
284 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
285 fixed_ct, altered_ct);
287 #endif /* DO_STATS */
288 return EXIT_SUCCESS;
292 void
293 do_version ()
295 static const char zFmt[] = "echo '%s'";
296 char zBuf[ 1024 ];
298 /* The 'version' option is really used to test that:
299 1. The program loads correctly (no missing libraries)
300 2. we can correctly run our server shell process
301 3. that we can compile all the regular expressions.
303 run_compiles ();
304 sprintf (zBuf, zFmt, program_id);
305 fputs (zBuf + 5, stdout);
306 exit (strcmp (run_shell (zBuf), program_id));
309 /* * * * * * * * * * * * */
311 void
312 initialize ()
314 static const char var_not_found[] =
315 "fixincl ERROR: %s environment variable not defined\n\
316 \tTARGET_MACHINE, DESTDIR, SRCDIR and FIND_BASE are required\n";
319 static const char var[] = "TARGET_MACHINE";
320 pz_machine = getenv (var);
321 if (pz_machine == (char *) NULL)
323 fprintf (stderr, var_not_found, var);
324 exit (EXIT_FAILURE);
329 static const char var[] = "DESTDIR";
330 pz_dest_dir = getenv (var);
331 if (pz_dest_dir == (char *) NULL)
333 fprintf (stderr, var_not_found, var);
334 exit (EXIT_FAILURE);
339 static const char var[] = "SRCDIR";
340 pz_src_dir = getenv (var);
341 if (pz_src_dir == (char *) NULL)
343 fprintf (stderr, var_not_found, var);
344 exit (EXIT_FAILURE);
349 static const char var[] = "VERBOSE";
350 char* pz = getenv (var);
351 if (pz != (char *) NULL)
353 if (isdigit( *pz ))
354 verbose_level = (te_verbose)atoi( pz );
355 else
356 switch (*pz) {
357 case 's':
358 case 'S':
359 verbose_level = VERB_SILENT; break;
361 case 'f':
362 case 'F':
363 verbose_level = VERB_FIXES; break;
365 case 'a':
366 case 'A':
367 verbose_level = VERB_APPLIES; break;
369 case 'p':
370 case 'P':
371 verbose_level = VERB_PROGRESS; break;
373 case 't':
374 case 'T':
375 verbose_level = VERB_TESTS; break;
377 case 'e':
378 case 'E':
379 verbose_level = VERB_EVERYTHING; break;
385 static const char var[] = "FIND_BASE";
386 char *pz = getenv (var);
387 if (pz == (char *) NULL)
389 fprintf (stderr, var_not_found, var);
390 exit (EXIT_FAILURE);
392 while ((pz[0] == '.') && (pz[1] == '/'))
393 pz += 2;
394 if ((pz[0] != '.') || (pz[1] != NUL))
395 find_base_len = strlen( pz );
398 /* Compile all the regular expressions now.
399 That way, it is done only once for the whole run.
401 run_compiles ();
403 signal (SIGQUIT, SIG_IGN);
404 signal (SIGIOT, SIG_IGN);
405 signal (SIGPIPE, SIG_IGN);
406 signal (SIGALRM, SIG_IGN);
407 signal (SIGTERM, SIG_IGN);
408 #ifndef NO_BOGOSITY
410 Make sure that if we opened a server process, we close it now.
411 This is the grandparent process. We don't need the server anymore
412 and our children should make their own. */
414 close_server ();
415 (void)wait ( (int*)NULL );
416 #endif
419 #ifndef NO_BOGOSITY
420 /* * * * * * * * * * * * *
422 wait_for_pid - Keep calling `wait(2)' until it returns
423 the process id we are looking for. Not every system has
424 `waitpid(2)'. We also ensure that the children exit with success. */
426 void
427 wait_for_pid(child)
428 pid_t child;
430 for (;;) {
431 int status;
432 pid_t dead_kid = wait (&status);
434 if (dead_kid == child)
436 if (! WIFEXITED( status ))
438 if (WSTOPSIG( status ) == 0)
439 break;
441 fprintf (stderr, "child process %d is hung on signal %d\n",
442 child, WSTOPSIG( status ));
443 exit (EXIT_FAILURE);
445 if (WEXITSTATUS( status ) != 0)
447 fprintf (stderr, "child process %d exited with status %d\n",
448 child, WEXITSTATUS( status ));
449 exit (EXIT_FAILURE);
451 break; /* normal child completion */
455 IF there is an error, THEN see if it is retryable.
456 If it is not retryable, then break out of this loop. */
457 if (dead_kid == NOPROCESS)
459 switch (errno) {
460 case EINTR:
461 case EAGAIN:
462 break;
464 default:
465 if (NOT_SILENT)
466 fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
467 errno, xstrerror( errno ), child );
468 /* FALLTHROUGH */
470 case ECHILD: /* no children to wait for?? */
471 return;
474 } done_waiting:;
476 #endif /* NO_BOGOSITY */
478 /* * * * * * * * * * * * *
480 load_file loads all the contents of a file into malloc-ed memory.
481 Its argument is the name of the file to read in; the returned
482 result is the NUL terminated contents of the file. The file
483 is presumed to be an ASCII text file containing no NULs. */
484 char *
485 load_file ( fname )
486 const char* fname;
488 struct stat stbf;
489 char* res;
491 if (stat (fname, &stbf) != 0)
493 if (NOT_SILENT)
494 fprintf (stderr, "error %d (%s) stat-ing %s\n",
495 errno, xstrerror (errno), fname );
496 return (char *) NULL;
498 if (stbf.st_size == 0)
499 return (char*)NULL;
501 data_map_size = stbf.st_size+1;
502 data_map_fd = open (fname, O_RDONLY);
503 ttl_data_size += data_map_size-1;
505 if (data_map_fd < 0)
507 if (NOT_SILENT)
508 fprintf (stderr, "error %d (%s) opening %s for read\n",
509 errno, xstrerror (errno), fname);
510 return (char*)NULL;
513 #ifdef HAVE_MMAP
514 curr_data_mapped = BOOL_TRUE;
515 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ, MAP_PRIVATE,
516 data_map_fd, 0);
517 if (res == (char*)BAD_ADDR)
519 curr_data_mapped = BOOL_FALSE;
520 res = load_file_data ( fdopen (data_map_fd, "r"));
522 #else
523 curr_data_mapped = BOOL_FALSE;
524 res = load_file_data ( fdopen (data_map_fd, "r"));
525 #endif
527 return res;
531 /* * * * * * * * * * * * *
533 run_compiles run all the regexp compiles for all the fixes once.
535 void
536 run_compiles ()
538 tFixDesc *p_fixd = fixDescList;
539 int fix_ct = FIX_COUNT;
540 tTestDesc *p_test;
541 int test_ct;
542 int re_ct = REGEX_COUNT;
543 const char *pz_err;
544 regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
546 if (p_re == (regex_t *) NULL)
548 fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n",
549 REGEX_COUNT * sizeof (regex_t));
550 exit (EXIT_FAILURE);
553 /* Make sure compile_re does not stumble across invalid data */
555 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
556 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
558 compile_re (incl_quote_pat, &incl_quote_re, 1,
559 "quoted include", "run_compiles");
561 /* FOR every fixup, ... */
564 p_test = p_fixd->p_test_desc;
565 test_ct = p_fixd->test_ct;
567 /* IF the machine type pointer is not NULL (we are not in test mode)
568 AND this test is for or not done on particular machines
569 THEN ... */
571 if ( (pz_machine != NULL)
572 && (p_fixd->papz_machs != (const char**) NULL) )
574 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
575 tSCC esac_fmt[] =
576 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
577 tSCC skip[] = "skip"; /* 4 bytes */
578 tSCC run[] = "run"; /* 3 bytes */
579 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
581 const char **papz_machs = p_fixd->papz_machs;
582 char *pz;
583 char *pz_sep = "";
584 tCC *pz_if_true;
585 tCC *pz_if_false;
586 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
588 /* Start the case statement */
590 sprintf (cmd_buf, case_fmt, pz_machine);
591 pz = cmd_buf + strlen (cmd_buf);
593 /* Determine if a match means to apply the fix or not apply it */
595 if (p_fixd->fd_flags & FD_MACH_IFNOT)
597 pz_if_true = skip;
598 pz_if_false = run;
600 else
602 pz_if_true = run;
603 pz_if_false = skip;
606 /* Emit all the machine names. If there are more than one,
607 then we will insert " | \\\n" between the names */
609 for (;;)
611 const char* pz_mach = *(papz_machs++);
613 if (pz_mach == (const char*) NULL)
614 break;
615 sprintf (pz, "%s%s", pz_sep, pz_mach);
616 pz += strlen (pz);
617 pz_sep = " | \\\n";
620 /* Now emit the match and not-match actions and the esac */
622 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
624 /* Run the script.
625 The result will start either with 's' or 'r'. */
628 int skip;
629 pz = run_shell (cmd_buf);
630 skip = (*pz == 's');
631 free ( (void*)pz );
632 if (skip)
634 p_fixd->fd_flags |= FD_SKIP_TEST;
635 continue;
640 /* FOR every test for the fixup, ... */
642 while (--test_ct >= 0)
644 switch (p_test->type)
646 case TT_EGREP:
647 case TT_NEGREP:
648 /* You might consider putting the following under #ifdef.
649 The number of re's used is computed by autogen.
650 So, it is static and known at compile time. */
652 if (--re_ct < 0)
654 fputs ("out of RE's\n", stderr);
655 exit (EXIT_FAILURE);
658 p_test->p_test_regex = p_re++;
659 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
660 "select test", p_fixd->fix_name);
662 p_test++;
665 while (p_fixd++, --fix_ct > 0);
669 /* * * * * * * * * * * * *
671 create_file Create the output modified file.
672 Input: the name of the file to create
673 Returns: a file pointer to the new, open file */
675 #if defined(S_IRUSR) && defined(S_IWUSR) && \
676 defined(S_IRGRP) && defined(S_IROTH)
678 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
679 #else
680 # define S_IRALL 0644
681 #endif
683 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
684 defined(S_IROTH) && defined(S_IXOTH)
686 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
687 #else
688 # define S_DIRALL 0755
689 #endif
692 FILE *
693 create_file ()
695 int fd;
696 FILE *pf;
697 char fname[MAXPATHLEN];
699 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
701 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
703 /* We may need to create the directories needed... */
704 if ((fd < 0) && (errno == ENOENT))
706 char *pz_dir = strchr (fname + 1, '/');
707 struct stat stbf;
709 while (pz_dir != (char *) NULL)
711 *pz_dir = NUL;
712 if (stat (fname, &stbf) < 0)
714 mkdir (fname, S_IFDIR | S_DIRALL);
717 *pz_dir = '/';
718 pz_dir = strchr (pz_dir + 1, '/');
721 /* Now, lets try the open again... */
722 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
724 if (fd < 0)
726 fprintf (stderr, "Error %d (%s) creating %s\n",
727 errno, xstrerror (errno), fname);
728 exit (EXIT_FAILURE);
730 if (NOT_SILENT)
731 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
732 pf = fdopen (fd, "w");
734 #ifdef LATER
736 static const char hdr[] =
737 "/* DO NOT EDIT THIS FILE.\n\n"
738 " It has been auto-edited by fixincludes from /usr/include/%s\n"
739 " This had to be done to correct non-standard usages in the\n"
740 " original, manufacturer supplied header file. */\n\n";
742 fprintf (pf, hdr, pz_curr_file);
744 #endif
745 return pf;
749 /* * * * * * * * * * * * *
751 test_test make sure a shell-style test expression passes.
752 Input: a pointer to the descriptor of the test to run and
753 the name of the file that we might want to fix
754 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
755 shell script we run. */
758 test_test (p_test, pz_test_file)
759 tTestDesc *p_test;
760 char* pz_test_file;
762 tSCC cmd_fmt[] =
763 "file=%s\n\
764 if ( test %s ) > /dev/null 2>&1\n\
765 then echo TRUE\n\
766 else echo FALSE\n\
767 fi";
769 char *pz_res;
770 int res = SKIP_FIX;
772 static char cmd_buf[4096];
774 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
775 pz_res = run_shell (cmd_buf);
776 if (*pz_res == 'T')
777 res = APPLY_FIX;
778 free ((void *) pz_res);
779 return res;
783 /* * * * * * * * * * * * *
785 egrep_test make sure an egrep expression is found in the file text.
786 Input: a pointer to the descriptor of the test to run and
787 the pointer to the contents of the file under suspicion
788 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
790 The caller may choose to reverse meaning if the sense of the test
791 is inverted. */
794 egrep_test (pz_data, p_test)
795 char *pz_data;
796 tTestDesc *p_test;
798 #ifdef DEBUG
799 if (p_test->p_test_regex == 0)
800 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
801 p_test->pz_test_text);
802 #endif
803 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
804 return APPLY_FIX;
805 return SKIP_FIX;
809 /* * * * * * * * * * * * *
811 quoted_file_exists Make sure that a file exists before we emit
812 the file name. If we emit the name, our invoking shell will try
813 to copy a non-existing file into the destination directory. */
816 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
817 char* pz_src_path;
818 char* pz_file_path;
819 char* pz_file;
821 char z[ MAXPATHLEN ];
822 char* pz;
823 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
824 pz = z + strlen ( z );
826 for (;;) {
827 char ch = *pz_file++;
828 if (! ISGRAPH( ch ))
829 return 0;
830 if (ch == '"')
831 break;
832 *pz++ = ch;
834 *pz = '\0';
836 struct stat s;
837 if (stat (z, &s) != 0)
838 return 0;
839 return S_ISREG( s.st_mode );
844 /* * * * * * * * * * * * *
846 extract_quoted_files
848 The syntax, `#include "file.h"' specifies that the compiler is to
849 search the local directory of the current file before the include
850 list. Consequently, if we have modified a header and stored it in
851 another directory, any files that are included by that modified
852 file in that fashion must also be copied into this new directory.
853 This routine finds those flavors of #include and for each one found
854 emits a triple of:
856 1. source directory of the original file
857 2. the relative path file name of the #includ-ed file
858 3. the full destination path for this file
860 Input: the text of the file, the file name and a pointer to the
861 match list where the match information was stored.
862 Result: internally nothing. The results are written to stdout
863 for interpretation by the invoking shell */
866 void
867 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
868 char *pz_data;
869 const char *pz_fixed_file;
870 regmatch_t *p_re_match;
872 char *pz_dir_end = strrchr (pz_fixed_file, '/');
873 char *pz_incl_quot = pz_data;
875 if (VLEVEL( VERB_APPLIES ))
876 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
878 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
879 If there is none, then it is in our current directory, ".". */
881 if (pz_dir_end == (char *) NULL)
882 pz_fixed_file = ".";
883 else
884 *pz_dir_end = '\0';
886 for (;;)
888 pz_incl_quot += p_re_match->rm_so;
890 /* Skip forward to the included file name */
891 while (ISSPACE (*pz_incl_quot))
892 pz_incl_quot++;
893 /* ISSPACE() may evaluate is argument more than once! */
894 while (++pz_incl_quot, ISSPACE (*pz_incl_quot))
896 pz_incl_quot += sizeof ("include") - 1;
897 while (*pz_incl_quot++ != '"')
900 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
902 /* Print the source directory and the subdirectory
903 of the file in question. */
904 printf ("%s %s/", pz_src_dir, pz_fixed_file);
905 pz_dir_end = pz_incl_quot;
907 /* Append to the directory the relative path of the desired file */
908 while (*pz_incl_quot != '"')
909 putc (*pz_incl_quot++, stdout);
911 /* Now print the destination directory appended with the
912 relative path of the desired file */
913 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
914 while (*pz_dir_end != '"')
915 putc (*pz_dir_end++, stdout);
917 /* End of entry */
918 putc ('\n', stdout);
921 /* Find the next entry */
922 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
923 break;
928 /* * * * * * * * * * * * *
930 Somebody wrote a *_fix subroutine that we must call.
934 internal_fix (read_fd, p_fixd)
935 int read_fd;
936 tFixDesc* p_fixd;
938 int fd[2];
940 if (pipe( fd ) != 0)
942 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
943 exit (EXIT_FAILURE);
946 for (;;)
948 pid_t childid = fork();
950 switch (childid)
952 case -1:
953 break;
955 case 0:
956 close (fd[0]);
957 goto do_child_task;
959 default:
961 * Parent process
963 close (read_fd);
964 close (fd[1]);
965 return fd[0];
969 * Parent in error
971 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
972 p_fixd->fix_name);
974 static int failCt = 0;
975 if ((errno != EAGAIN) || (++failCt > 10))
976 exit (EXIT_FAILURE);
977 sleep (1);
979 } do_child_task:;
982 * Close our current stdin and stdout
984 close (STDIN_FILENO);
985 close (STDOUT_FILENO);
986 UNLOAD_DATA();
989 * Make the fd passed in the stdin, and the write end of
990 * the new pipe become the stdout.
992 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
993 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
994 fdopen (STDIN_FILENO, "r");
995 fdopen (STDOUT_FILENO, "w");
997 apply_fix (p_fixd->patch_args[0], pz_curr_file);
998 exit (0);
1002 /* * * * * * * * * * * * *
1004 This loop should only cycle for 1/2 of one loop.
1005 "chain_open" starts a process that uses "read_fd" as
1006 its stdin and returns the new fd this process will use
1007 for stdout. */
1010 start_fixer (read_fd, p_fixd, pz_fix_file)
1011 int read_fd;
1012 tFixDesc* p_fixd;
1013 char* pz_fix_file;
1015 tCC* pz_cmd_save;
1016 char* pz_cmd;
1018 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1019 return internal_fix (read_fd, p_fixd);
1021 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1022 pz_cmd = (char*)NULL;
1023 else
1025 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1026 pz_cmd = (char*)malloc (strlen (p_fixd->patch_args[2])
1027 + sizeof( z_cmd_fmt )
1028 + strlen( pz_fix_file ));
1029 if (pz_cmd == (char*)NULL)
1031 fputs ("allocation failure\n", stderr);
1032 exit (EXIT_FAILURE);
1034 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1035 pz_cmd_save = p_fixd->patch_args[2];
1036 p_fixd->patch_args[2] = pz_cmd;
1039 /* Start a fix process, handing off the previous read fd for its
1040 stdin and getting a new fd that reads from the fix process' stdout.
1041 We normally will not loop, but we will up to 10 times if we keep
1042 getting "EAGAIN" errors.
1045 for (;;)
1047 static int failCt = 0;
1048 int fd;
1050 fd = chain_open (read_fd,
1051 (t_pchar *) p_fixd->patch_args,
1052 (process_chain_head == -1)
1053 ? &process_chain_head : (pid_t *) NULL);
1055 if (fd != -1)
1057 read_fd = fd;
1058 break;
1061 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1062 p_fixd->fix_name);
1064 if ((errno != EAGAIN) || (++failCt > 10))
1065 exit (EXIT_FAILURE);
1066 sleep (1);
1069 /* IF we allocated a shell script command,
1070 THEN free it and restore the command format to the fix description */
1071 if (pz_cmd != (char*)NULL)
1073 free ((void*)pz_cmd);
1074 p_fixd->patch_args[2] = pz_cmd_save;
1077 return read_fd;
1081 /* * * * * * * * * * * * *
1083 Process the potential fixes for a particular include file.
1084 Input: the original text of the file and the file's name
1085 Result: none. A new file may or may not be created. */
1087 t_bool
1088 fix_applies (p_fixd)
1089 tFixDesc *p_fixd;
1091 #ifdef DEBUG
1092 static const char z_failed[] = "not applying %s to %s - test %d failed\n";
1093 #endif
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 if (p_fixd->fd_flags & FD_SKIP_TEST)
1100 return BOOL_FALSE;
1102 /* IF there is a file name restriction,
1103 THEN ensure the current file name matches one in the pattern */
1105 if (pz_scan != (char *) NULL)
1107 size_t name_len;
1109 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1110 pz_fname += 2;
1111 name_len = strlen (pz_fname);
1113 for (;;)
1115 pz_scan = strstr (pz_scan + 1, pz_fname);
1116 /* IF we can't match the string at all,
1117 THEN bail */
1118 if (pz_scan == (char *) NULL) {
1119 #ifdef DEBUG
1120 if (VLEVEL( VERB_EVERYTHING ))
1121 fprintf (stderr, "file %s not in list for %s\n",
1122 pz_fname, p_fixd->fix_name );
1123 #endif
1124 return BOOL_FALSE;
1127 /* IF the match is surrounded by the '|' markers,
1128 THEN we found a full match -- time to run the tests */
1130 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1131 break;
1135 /* FOR each test, see if it fails.
1136 IF it does fail, then we go on to the next test */
1138 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1139 test_ct-- > 0;
1140 p_test++)
1142 switch (p_test->type)
1144 case TT_TEST:
1145 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1146 #ifdef DEBUG
1147 if (VLEVEL( VERB_EVERYTHING ))
1148 fprintf (stderr, z_failed, p_fixd->fix_name, pz_fname,
1149 p_fixd->test_ct - test_ct);
1150 #endif
1151 return BOOL_FALSE;
1153 break;
1155 case TT_EGREP:
1156 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1157 #ifdef DEBUG
1158 if (VLEVEL( VERB_EVERYTHING ))
1159 fprintf (stderr, z_failed, p_fixd->fix_name, pz_fname,
1160 p_fixd->test_ct - test_ct);
1161 #endif
1162 return BOOL_FALSE;
1164 break;
1166 case TT_NEGREP:
1167 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1168 #ifdef DEBUG
1169 if (VLEVEL( VERB_EVERYTHING ))
1170 fprintf (stderr, z_failed, p_fixd->fix_name, pz_fname,
1171 p_fixd->test_ct - test_ct);
1172 #endif
1173 /* Negated sense */
1174 return BOOL_FALSE;
1176 break;
1178 case TT_FUNCTION:
1179 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1180 != APPLY_FIX) {
1181 #ifdef DEBUG
1182 if (VLEVEL( VERB_EVERYTHING ))
1183 fprintf (stderr, z_failed, p_fixd->fix_name, pz_fname,
1184 p_fixd->test_ct - test_ct);
1185 #endif
1186 return BOOL_FALSE;
1188 break;
1192 return BOOL_TRUE;
1196 /* * * * * * * * * * * * *
1198 Write out a replacement file */
1200 void
1201 write_replacement (p_fixd)
1202 tFixDesc *p_fixd;
1204 const char* pz_text = p_fixd->patch_args[0];
1206 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1207 return;
1210 FILE* out_fp = create_file (pz_curr_file);
1211 fputs (pz_text, out_fp);
1212 fclose (out_fp);
1217 /* * * * * * * * * * * * *
1219 We have work to do. Read back in the output
1220 of the filtering chain. Compare each byte as we read it with
1221 the contents of the original file. As soon as we find any
1222 difference, we will create the output file, write out all
1223 the matched text and then copy any remaining data from the
1224 output of the filter chain.
1226 void
1227 test_for_changes (read_fd)
1228 int read_fd;
1230 FILE *in_fp = fdopen (read_fd, "r");
1231 FILE *out_fp = (FILE *) NULL;
1232 char *pz_cmp = pz_curr_data;
1234 #ifdef DO_STATS
1235 fixed_ct++;
1236 #endif
1237 for (;;)
1239 int ch;
1241 ch = getc (in_fp);
1242 if (ch == EOF)
1243 break;
1245 /* IF we are emitting the output
1246 THEN emit this character, too.
1248 if (out_fp != (FILE *) NULL)
1249 putc (ch, out_fp);
1251 /* ELSE if this character does not match the original,
1252 THEN now is the time to start the output.
1254 else if (ch != *pz_cmp)
1256 out_fp = create_file (pz_curr_file);
1258 #ifdef DO_STATS
1259 altered_ct++;
1260 #endif
1261 /* IF there are matched data, write the matched part now. */
1262 if (pz_cmp != pz_curr_data)
1263 fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
1265 /* Emit the current unmatching character */
1266 putc (ch, out_fp);
1268 else
1269 /* ELSE the character matches. Advance the compare ptr */
1270 pz_cmp++;
1273 /* IF we created the output file, ... */
1274 if (out_fp != (FILE *) NULL)
1276 regmatch_t match;
1278 /* Close the file and see if we have to worry about
1279 `#include "file.h"' constructs. */
1280 fclose (out_fp);
1281 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1282 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1285 fclose (in_fp);
1286 close (read_fd); /* probably redundant, but I'm paranoid */
1290 /* * * * * * * * * * * * *
1292 Process the potential fixes for a particular include file.
1293 Input: the original text of the file and the file's name
1294 Result: none. A new file may or may not be created. */
1296 void
1297 process ()
1299 static char env_current_file[1024];
1300 tFixDesc *p_fixd = fixDescList;
1301 int todo_ct = FIX_COUNT;
1302 int read_fd = -1;
1303 int num_children = 0;
1305 if (access (pz_curr_file, R_OK) != 0)
1307 int erno = errno;
1308 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1309 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1310 erno, xstrerror (erno));
1311 return;
1314 pz_curr_data = load_file (pz_curr_file);
1315 if (pz_curr_data == (char *) NULL)
1316 return;
1318 #ifdef DO_STATS
1319 process_ct++;
1320 #endif
1321 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1322 fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
1324 process_chain_head = NOPROCESS;
1326 /* For every fix in our fix list, ... */
1327 for (; todo_ct > 0; p_fixd++, todo_ct--)
1329 if (! fix_applies (p_fixd))
1330 continue;
1332 if (VLEVEL( VERB_APPLIES ))
1333 fprintf (stderr, "Applying %-24s to %s\n",
1334 p_fixd->fix_name, pz_curr_file);
1336 if (p_fixd->fd_flags & FD_REPLACEMENT)
1338 write_replacement (p_fixd);
1339 UNLOAD_DATA();
1340 return;
1343 /* IF we do not have a read pointer,
1344 THEN this is the first fix for the current file.
1345 Open the source file. That will be used as stdin for
1346 the first fix. Any subsequent fixes will use the
1347 stdout descriptor of the previous fix for its stdin. */
1349 if (read_fd == -1)
1351 read_fd = open (pz_curr_file, O_RDONLY);
1352 if (read_fd < 0)
1354 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1355 xstrerror (errno), pz_curr_file);
1356 exit (EXIT_FAILURE);
1359 /* Ensure we do not get duplicate output */
1361 fflush (stdout);
1364 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1365 num_children++;
1368 /* IF we have a read-back file descriptor,
1369 THEN check for changes and write output if changed. */
1371 if (read_fd >= 0)
1373 test_for_changes (read_fd);
1374 #ifdef DO_STATS
1375 apply_ct += num_children;
1376 #endif
1377 /* Wait for child processes created by chain_open()
1378 to avoid leaving zombies. */
1379 do {
1380 wait ((int *) NULL);
1381 } while (--num_children > 0);
1384 UNLOAD_DATA();