]> git.ipfire.org Git - thirdparty/gcc.git/blob - fixincludes/fixincl.c
patch to fix extraneous nop at function start, bug from David Mosberger
[thirdparty/gcc.git] / fixincludes / fixincl.c
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.
4
5 Copyright (C) 1997, 1998, 1999, 2000, 2004 Free Software Foundation, Inc.
6
7 This file is part of GCC.
8
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.
13
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.
18
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. */
23
24 #include "fixlib.h"
25
26 #include <sys/stat.h>
27
28 #if defined( HAVE_MMAP_FILE )
29 #include <sys/mman.h>
30 #define BAD_ADDR ((void*)-1)
31 #endif
32
33 #ifndef SEPARATE_FIX_PROC
34 #include "server.h"
35 #endif
36
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. */
39
40 static const char program_id[] = "fixincl version 1.1";
41
42 /* This format will be used at the start of every generated file */
43
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";
50
51 int find_base_len = 0;
52
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;
61
62 te_verbose verbose_level = VERB_PROGRESS;
63 int have_tty = 0;
64
65 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
66 #define NOT_SILENT VLEVEL(VERB_FIXES)
67
68 pid_t process_chain_head = (pid_t) -1;
69
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;
78
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 */
85
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;
89
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);
95
96 /* External Source Code */
97
98 #include "fixincl.x"
99
100 /* * * * * * * * * * * * * * * * * * *
101 *
102 * MAIN ROUTINE
103 */
104 extern int main (int, char **);
105 int
106 main (int argc, char** argv)
107 {
108 char *file_name_buf;
109
110 initialize ( argc, argv );
111
112 have_tty = isatty (fileno (stderr));
113
114 /* Before anything else, ensure we can allocate our file name buffer. */
115 file_name_buf = load_file_data (stdin);
116
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 */
120
121 freopen ("/dev/null", "r", stdin);
122
123 if (file_name_buf == (char *) NULL)
124 {
125 fputs ("No file names listed for fixing\n", stderr);
126 exit (EXIT_FAILURE);
127 }
128
129 for (;;)
130 {
131 char* pz_end;
132
133 /* skip to start of name, past any "./" prefixes */
134
135 while (ISSPACE (*file_name_buf)) file_name_buf++;
136 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
137 file_name_buf += 2;
138
139 /* Check for end of list */
140
141 if (*file_name_buf == NUL)
142 break;
143
144 /* Set global file name pointer and find end of name */
145
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;
152
153 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
154
155 /* IF no name is found (blank line) or comment marker, skip line */
156
157 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
158 continue;
159 *pz_end = NUL;
160
161 process ();
162 } /* for (;;) */
163
164 #ifdef DO_STATS
165 if (VLEVEL( VERB_PROGRESS )) {
166 tSCC zFmt[] =
167 "\
168 Processed %5d files containing %d bytes \n\
169 Applying %5d fixes to %d files\n\
170 Altering %5d of them\n";
171
172 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
173 fixed_ct, altered_ct);
174 }
175 #endif /* DO_STATS */
176
177 # ifdef SEPARATE_FIX_PROC
178 unlink( pz_temp_file );
179 # endif
180 exit (EXIT_SUCCESS);
181 }
182
183
184 static void
185 do_version (void)
186 {
187 static const char zFmt[] = "echo '%s'";
188 char zBuf[ 1024 ];
189
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
194 */
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
203 }
204
205 /* * * * * * * * * * * * */
206
207 void
208 initialize ( int argc, char** argv )
209 {
210 xmalloc_set_program_name (argv[0]);
211
212 switch (argc)
213 {
214 case 1:
215 break;
216
217 case 2:
218 if (strcmp (argv[1], "-v") == 0)
219 do_version ();
220 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
221 {
222 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
223 errno, xstrerror (errno), argv[1] );
224 exit (EXIT_FAILURE);
225 }
226 break;
227
228 default:
229 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
230 exit (EXIT_FAILURE);
231 }
232
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
238
239 initialize_opts ();
240
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;
248
249 case 'f':
250 case 'F':
251 verbose_level = VERB_FIXES; break;
252
253 case 'a':
254 case 'A':
255 verbose_level = VERB_APPLIES; break;
256
257 default:
258 case 'p':
259 case 'P':
260 verbose_level = VERB_PROGRESS; break;
261
262 case 't':
263 case 'T':
264 verbose_level = VERB_TESTS; break;
265
266 case 'e':
267 case 'E':
268 verbose_level = VERB_EVERYTHING; break;
269 }
270 if (verbose_level >= VERB_EVERYTHING) {
271 verbose_level = VERB_EVERYTHING;
272 fputs ("fixinc verbosity: EVERYTHING\n", stderr);
273 }
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 );
278
279 /* Compile all the regular expressions now.
280 That way, it is done only once for the whole run.
281 */
282 run_compiles ();
283
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
289
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);
295 }
296
297 /* * * * * * * * * * * * *
298
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 )
305 {
306 struct stat stbf;
307 char* res;
308
309 if (stat (fname, &stbf) != 0)
310 {
311 if (NOT_SILENT)
312 fprintf (stderr, "error %d (%s) stat-ing %s\n",
313 errno, xstrerror (errno), fname );
314 return (char *) NULL;
315 }
316 if (stbf.st_size == 0)
317 return (char*)NULL;
318
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;
326
327 if (data_map_fd < 0)
328 {
329 if (NOT_SILENT)
330 fprintf (stderr, "error %d (%s) opening %s for read\n",
331 errno, xstrerror (errno), fname);
332 return (char*)NULL;
333 }
334
335 #ifdef HAVE_MMAP_FILE
336 curr_data_mapped = BOOL_TRUE;
337
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
347 {
348 FILE* fp = fdopen (data_map_fd, "r");
349 curr_data_mapped = BOOL_FALSE;
350 res = load_file_data (fp);
351 fclose (fp);
352 }
353
354 return res;
355 }
356
357 static int
358 machine_matches( tFixDesc* p_fixd )
359 {
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 */
367
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 */
374
375 /* Start the case statement */
376
377 sprintf (cmd_buf, case_fmt, pz_machine);
378 pz = cmd_buf + strlen (cmd_buf);
379
380 /* Determine if a match means to apply the fix or not apply it */
381
382 if (p_fixd->fd_flags & FD_MACH_IFNOT)
383 {
384 pz_if_true = skip;
385 pz_if_false = run;
386 }
387 else
388 {
389 pz_if_true = run;
390 pz_if_false = skip;
391 }
392
393 /* Emit all the machine names. If there are more than one,
394 then we will insert " | \\\n" between the names */
395
396 for (;;)
397 {
398 const char* pz_mach = *(papz_machs++);
399
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";
405 }
406
407 /* Now emit the match and not-match actions and the esac */
408
409 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
410
411 /* Run the script.
412 The result will start either with 's' or 'r'. */
413
414 {
415 int skip;
416 pz = run_shell (cmd_buf);
417 skip = (*pz == 's');
418 free ( (void*)pz );
419 if (skip)
420 {
421 p_fixd->fd_flags |= FD_SKIP_TEST;
422 return BOOL_FALSE;
423 }
424 }
425
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 (;;)
431 {
432 const char* pz_mach = *(papz_machs++);
433
434 if (pz_mach == (const char*) NULL)
435 break;
436 if (strstr (pz_mach, "dos") != NULL && !invert)
437 return BOOL_TRUE;
438 }
439
440 p_fixd->fd_flags |= FD_SKIP_TEST;
441 return BOOL_FALSE;
442 # endif
443 }
444
445 /* * * * * * * * * * * * *
446
447 run_compiles run all the regexp compiles for all the fixes once.
448 */
449 void
450 run_compiles (void)
451 {
452 tFixDesc *p_fixd = fixDescList;
453 int fix_ct = FIX_COUNT;
454 regex_t *p_re = xcalloc (REGEX_COUNT, sizeof (regex_t));
455
456 /* Make sure compile_re does not stumble across invalid data */
457
458 memset (&incl_quote_re, '\0', sizeof (regex_t));
459
460 compile_re (incl_quote_pat, &incl_quote_re, 1,
461 "quoted include", "run_compiles");
462
463 /* Allow machine name tests to be ignored (testing, mainly) */
464
465 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
466 pz_machine = (char*)NULL;
467
468 /* FOR every fixup, ... */
469 do
470 {
471 tTestDesc *p_test = p_fixd->p_test_desc;
472 int test_ct = p_fixd->test_ct;
473
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 ... */
477
478 if ( (pz_machine != NULL)
479 && (p_fixd->papz_machs != (const char**) NULL)
480 && ! machine_matches (p_fixd) )
481 continue;
482
483 /* FOR every test for the fixup, ... */
484
485 while (--test_ct >= 0)
486 {
487 switch (p_test->type)
488 {
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;
495 }
496 p_test++;
497 }
498 }
499 while (p_fixd++, --fix_ct > 0);
500 }
501
502
503 /* * * * * * * * * * * * *
504
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 */
508
509 #if defined(S_IRUSR) && defined(S_IWUSR) && \
510 defined(S_IRGRP) && defined(S_IROTH)
511
512 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
513 #else
514 # define S_IRALL 0644
515 #endif
516
517 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
518 defined(S_IROTH) && defined(S_IXOTH)
519
520 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
521 #else
522 # define S_DIRALL 0755
523 #endif
524
525
526 static FILE *
527 create_file (void)
528 {
529 int fd;
530 FILE *pf;
531 char fname[MAXPATHLEN];
532
533 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
534
535 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
536
537 /* We may need to create the directories needed... */
538 if ((fd < 0) && (errno == ENOENT))
539 {
540 char *pz_dir = strchr (fname + 1, '/');
541 struct stat stbf;
542
543 while (pz_dir != (char *) NULL)
544 {
545 *pz_dir = NUL;
546 if (stat (fname, &stbf) < 0)
547 {
548 #ifdef _WIN32
549 mkdir (fname);
550 #else
551 mkdir (fname, S_IFDIR | S_DIRALL);
552 #endif
553 }
554
555 *pz_dir = '/';
556 pz_dir = strchr (pz_dir + 1, '/');
557 }
558
559 /* Now, lets try the open again... */
560 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
561 }
562 if (fd < 0)
563 {
564 fprintf (stderr, "Error %d (%s) creating %s\n",
565 errno, xstrerror (errno), fname);
566 exit (EXIT_FAILURE);
567 }
568 if (NOT_SILENT)
569 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
570 pf = fdopen (fd, "w");
571
572 /*
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.
575 */
576 fprintf (pf, z_std_preamble,
577 (pz_machine == NULL)
578 ? "fixinc/tests/inc"
579 : pz_input_dir,
580 pz_curr_file);
581
582 return pf;
583 }
584
585
586 /* * * * * * * * * * * * *
587
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)
596 {
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";
603
604 char *pz_res;
605 int res;
606
607 static char cmd_buf[4096];
608
609 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
610 pz_res = run_shell (cmd_buf);
611
612 switch (*pz_res) {
613 case 'T':
614 res = APPLY_FIX;
615 break;
616
617 case 'F':
618 res = SKIP_FIX;
619 break;
620
621 default:
622 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
623 pz_res, cmd_buf );
624 res = SKIP_FIX;
625 }
626
627 free ((void *) pz_res);
628 return res;
629 }
630 #else
631 /*
632 * IF we are in MS-DOS land, then whatever shell-type test is required
633 * will, by definition, fail
634 */
635 #define test_test(t,tf) SKIP_FIX
636 #endif
637
638 /* * * * * * * * * * * * *
639
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
644
645 The caller may choose to reverse meaning if the sense of the test
646 is inverted. */
647
648 static int
649 egrep_test (char* pz_data, tTestDesc* p_test)
650 {
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;
659 }
660
661
662 /* * * * * * * * * * * * *
663
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. */
667
668 static int
669 quoted_file_exists (const char* pz_src_path,
670 const char* pz_file_path,
671 const char* pz_file)
672 {
673 char z[ MAXPATHLEN ];
674 char* pz;
675 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
676 pz = z + strlen ( z );
677
678 for (;;) {
679 char ch = *pz_file++;
680 if (! ISGRAPH( ch ))
681 return 0;
682 if (ch == '"')
683 break;
684 *pz++ = ch;
685 }
686 *pz = '\0';
687 {
688 struct stat s;
689 if (stat (z, &s) != 0)
690 return 0;
691 return S_ISREG( s.st_mode );
692 }
693 }
694
695
696 /* * * * * * * * * * * * *
697 *
698 extract_quoted_files
699
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:
707
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
711
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 */
716
717
718 static void
719 extract_quoted_files (char* pz_data,
720 const char* pz_fixed_file,
721 regmatch_t* p_re_match)
722 {
723 char *pz_dir_end = strrchr (pz_fixed_file, '/');
724 char *pz_incl_quot = pz_data;
725
726 if (VLEVEL( VERB_APPLIES ))
727 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
728
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, ".". */
731
732 if (pz_dir_end == (char *) NULL)
733 pz_fixed_file = ".";
734 else
735 *pz_dir_end = '\0';
736
737 for (;;)
738 {
739 pz_incl_quot += p_re_match->rm_so;
740
741 /* Skip forward to the included file name */
742 while (*pz_incl_quot != '"')
743 pz_incl_quot++;
744
745 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
746 {
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;
751
752 /* Append to the directory the relative path of the desired file */
753 while (*pz_incl_quot != '"')
754 putc (*pz_incl_quot++, stdout);
755
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);
761
762 /* End of entry */
763 putc ('\n', stdout);
764 }
765
766 /* Find the next entry */
767 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
768 break;
769 }
770 }
771
772
773 /* * * * * * * * * * * * *
774
775 Somebody wrote a *_fix subroutine that we must call.
776 */
777 #ifndef SEPARATE_FIX_PROC
778 static int
779 internal_fix (int read_fd, tFixDesc* p_fixd)
780 {
781 int fd[2];
782
783 if (pipe( fd ) != 0)
784 {
785 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
786 exit (EXIT_FAILURE);
787 }
788
789 for (;;)
790 {
791 pid_t childid = fork();
792
793 switch (childid)
794 {
795 case -1:
796 break;
797
798 case 0:
799 close (fd[0]);
800 goto do_child_task;
801
802 default:
803 /*
804 * Parent process
805 */
806 close (read_fd);
807 close (fd[1]);
808 return fd[0];
809 }
810
811 /*
812 * Parent in error
813 */
814 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
815 p_fixd->fix_name);
816 {
817 static int failCt = 0;
818 if ((errno != EAGAIN) || (++failCt > 10))
819 exit (EXIT_FAILURE);
820 sleep (1);
821 }
822 } do_child_task:;
823
824 /*
825 * Close our current stdin and stdout
826 */
827 close (STDIN_FILENO);
828 close (STDOUT_FILENO);
829 UNLOAD_DATA();
830
831 /*
832 * Make the fd passed in the stdin, and the write end of
833 * the new pipe become the stdout.
834 */
835 dup2 (fd[1], STDOUT_FILENO);
836 dup2 (read_fd, STDIN_FILENO);
837
838 apply_fix (p_fixd, pz_curr_file);
839 exit (0);
840 }
841 #endif /* !SEPARATE_FIX_PROC */
842
843
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)
850 {
851 char* pz_cmd;
852 char* pz_scan;
853 size_t argsize;
854
855 if (p_fixd->fd_flags & FD_SUBROUTINE)
856 {
857 static const char z_applyfix_prog[] =
858 "/../fixincludes/applyfix" EXE_EXT;
859
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);
867
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);
872
873 strcpy (pz_scan, z_applyfix_prog);
874
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)
878 {
879 strcpy (pz_scan, "/..");
880 strcpy (pz_scan+3, z_applyfix_prog);
881 }
882
883 pz_scan += strlen (pz_scan);
884
885 /*
886 * Now add the fix number and file names that may be needed
887 */
888 sprintf (pz_scan, " %ld '%s' '%s' '%s'", p_fixd - fixDescList,
889 pz_fix_file, pz_file_source, pz_temp_file);
890 }
891 else /* NOT an "internal" fix: */
892 {
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.
901
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;
914
915 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
916 + strlen( pz_file_source );
917 parg_size = argsize;
918
919
920 /*
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. :)
926 */
927 for (;;)
928 {
929 tCC* p_arg = *(ppArgs++);
930 if (p_arg == NULL)
931 break;
932 argsize += 24 + strlen( p_arg );
933 }
934
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;
940
941 ppArgs = p_fixd->patch_args;
942
943 /*
944 * Copy the program name, unquoted
945 */
946 {
947 tCC* pArg = *(ppArgs++);
948 for (;;)
949 {
950 char ch = *(pArg++);
951 if (ch == NUL)
952 break;
953 *(pz_scan++) = ch;
954 }
955 }
956
957 /*
958 * Copy the program arguments, quoted
959 */
960 for (;;)
961 {
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) );
969 /*
970 * Make sure we don't overflow the buffer due to sloppy
971 * size estimation.
972 */
973 while (pz_scan == (char*)NULL)
974 {
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) );
981 }
982 }
983
984 /*
985 * add the file machinations.
986 */
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
993 }
994 system( pz_cmd );
995 free( (void*)pz_cmd );
996 }
997
998 /* * * * * * * * * * * * *
999
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. */
1004
1005 #else /* is *NOT* SEPARATE_FIX_PROC */
1006 static int
1007 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
1008 {
1009 tCC* pz_cmd_save;
1010 char* pz_cmd;
1011
1012 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1013 return internal_fix (read_fd, p_fixd);
1014
1015 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1016 {
1017 pz_cmd = NULL;
1018 pz_cmd_save = NULL;
1019 }
1020 else
1021 {
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;
1028 }
1029
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.
1034
1035 */
1036 for (;;)
1037 {
1038 static int failCt = 0;
1039 int fd;
1040
1041 fd = chain_open (read_fd,
1042 (tCC **) p_fixd->patch_args,
1043 (process_chain_head == -1)
1044 ? &process_chain_head : (pid_t *) NULL);
1045
1046 if (fd != -1)
1047 {
1048 read_fd = fd;
1049 break;
1050 }
1051
1052 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1053 p_fixd->fix_name);
1054
1055 if ((errno != EAGAIN) || (++failCt > 10))
1056 exit (EXIT_FAILURE);
1057 sleep (1);
1058 }
1059
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)
1063 {
1064 free ((void*)pz_cmd);
1065 p_fixd->patch_args[2] = pz_cmd_save;
1066 }
1067
1068 return read_fd;
1069 }
1070 #endif
1071
1072
1073 /* * * * * * * * * * * * *
1074
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. */
1078
1079 static t_bool
1080 fix_applies (tFixDesc* p_fixd)
1081 {
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;
1086
1087 # ifdef SEPARATE_FIX_PROC
1088 /*
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.
1092 */
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
1099
1100 /* IF there is a file name restriction,
1101 THEN ensure the current file name matches one in the pattern */
1102
1103 if (pz_scan != (char *) NULL)
1104 {
1105 size_t name_len;
1106
1107 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1108 pz_fname += 2;
1109 name_len = strlen (pz_fname);
1110
1111 for (;;)
1112 {
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;
1118
1119 /* IF the match is surrounded by the '|' markers,
1120 THEN we found a full match -- time to run the tests */
1121
1122 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1123 break;
1124 }
1125 }
1126
1127 /* FOR each test, see if it fails.
1128 IF it does fail, then we go on to the next test */
1129
1130 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1131 test_ct-- > 0;
1132 p_test++)
1133 {
1134 switch (p_test->type)
1135 {
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;
1144 }
1145 break;
1146
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;
1155 }
1156 break;
1157
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;
1167 }
1168 break;
1169
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;
1179 }
1180 break;
1181 }
1182 }
1183
1184 return BOOL_TRUE;
1185 }
1186
1187
1188 /* * * * * * * * * * * * *
1189
1190 Write out a replacement file */
1191
1192 static void
1193 write_replacement (tFixDesc* p_fixd)
1194 {
1195 const char* pz_text = p_fixd->patch_args[0];
1196
1197 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1198 return;
1199
1200 {
1201 FILE* out_fp = create_file ();
1202 fputs (pz_text, out_fp);
1203 fclose (out_fp);
1204 }
1205 }
1206
1207
1208 /* * * * * * * * * * * * *
1209
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.
1216 */
1217 static void
1218 test_for_changes (int read_fd)
1219 {
1220 FILE *in_fp = fdopen (read_fd, "r");
1221 FILE *out_fp = (FILE *) NULL;
1222 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1223
1224 #ifdef DO_STATS
1225 fixed_ct++;
1226 #endif
1227 for (;;)
1228 {
1229 int ch;
1230
1231 ch = getc (in_fp);
1232 if (ch == EOF)
1233 break;
1234 ch &= 0xFF; /* all bytes are 8 bits */
1235
1236 /* IF we are emitting the output
1237 THEN emit this character, too.
1238 */
1239 if (out_fp != (FILE *) NULL)
1240 putc (ch, out_fp);
1241
1242 /* ELSE if this character does not match the original,
1243 THEN now is the time to start the output.
1244 */
1245 else if (ch != *pz_cmp)
1246 {
1247 out_fp = create_file ();
1248
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);
1256
1257 /* Emit the current unmatching character */
1258 putc (ch, out_fp);
1259 }
1260 else
1261 /* ELSE the character matches. Advance the compare ptr */
1262 pz_cmp++;
1263 }
1264
1265 /* IF we created the output file, ... */
1266 if (out_fp != (FILE *) NULL)
1267 {
1268 regmatch_t match;
1269
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);
1275 }
1276
1277 fclose (in_fp);
1278 close (read_fd); /* probably redundant, but I'm paranoid */
1279 }
1280
1281
1282 /* * * * * * * * * * * * *
1283
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. */
1287
1288 void
1289 process (void)
1290 {
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
1299
1300 if (access (pz_curr_file, R_OK) != 0)
1301 {
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;
1307 }
1308
1309 pz_curr_data = load_file (pz_curr_file);
1310 if (pz_curr_data == (char *) NULL)
1311 return;
1312
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);
1319
1320 # ifndef SEPARATE_FIX_PROC
1321 process_chain_head = NOPROCESS;
1322
1323 /* For every fix in our fix list, ... */
1324 for (; todo_ct > 0; p_fixd++, todo_ct--)
1325 {
1326 if (! fix_applies (p_fixd))
1327 continue;
1328
1329 if (VLEVEL( VERB_APPLIES ))
1330 fprintf (stderr, "Applying %-24s to %s\n",
1331 p_fixd->fix_name, pz_curr_file);
1332
1333 if (p_fixd->fd_flags & FD_REPLACEMENT)
1334 {
1335 write_replacement (p_fixd);
1336 UNLOAD_DATA();
1337 return;
1338 }
1339
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. */
1345
1346 if (read_fd == -1)
1347 {
1348 read_fd = open (pz_curr_file, O_RDONLY);
1349 if (read_fd < 0)
1350 {
1351 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1352 xstrerror (errno), pz_curr_file);
1353 exit (EXIT_FAILURE);
1354 }
1355
1356 /* Ensure we do not get duplicate output */
1357
1358 fflush (stdout);
1359 }
1360
1361 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1362 num_children++;
1363 }
1364
1365 /* IF we have a read-back file descriptor,
1366 THEN check for changes and write output if changed. */
1367
1368 if (read_fd >= 0)
1369 {
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);
1379 }
1380
1381 # else /* is SEPARATE_FIX_PROC */
1382
1383 for (; todo_ct > 0; p_fixd++, todo_ct--)
1384 {
1385 if (! fix_applies (p_fixd))
1386 continue;
1387
1388 if (VLEVEL( VERB_APPLIES ))
1389 fprintf (stderr, "Applying %-24s to %s\n",
1390 p_fixd->fix_name, pz_curr_file);
1391
1392 if (p_fixd->fd_flags & FD_REPLACEMENT)
1393 {
1394 write_replacement (p_fixd);
1395 UNLOAD_DATA();
1396 return;
1397 }
1398 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1399 pz_file_source = pz_temp_file;
1400 }
1401
1402 read_fd = open (pz_temp_file, O_RDONLY);
1403 if (read_fd < 0)
1404 {
1405 if (errno != ENOENT)
1406 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1407 errno, xstrerror (errno), pz_temp_file);
1408 }
1409 else
1410 {
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);
1416 }
1417
1418 # endif
1419 UNLOAD_DATA();
1420 }