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