]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/fixinc/fixincl.c
aclocal.m4: Re-write, defining LIBU77_GETTIMEOFDAY, not LIBU77_HAVE_STRUCT_TIMEZONE.
[thirdparty/gcc.git] / gcc / fixinc / fixincl.c
CommitLineData
0083c904 1
1f414ac4
BK
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.
5
6 Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
7
8This file is part of GNU CC.
9
10GNU CC is free software; you can redistribute it and/or modify
11it under the terms of the GNU General Public License as published by
12the Free Software Foundation; either version 2, or (at your option)
13any later version.
14
15GNU CC is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with GNU CC; see the file COPYING. If not, write to
22the Free Software Foundation, 59 Temple Place - Suite 330,
23Boston, MA 02111-1307, USA. */
24
7b33bb99 25#include "auto-host.h"
1f414ac4 26
2583397b
RL
27#include "gansidecl.h"
28#include "system.h"
0083c904 29#include <signal.h>
0083c904 30
f7d920fd 31#include "gnu-regex.h"
0083c904
BK
32#include "server.h"
33
1f414ac4
BK
34static const char program_id[] = "fixincl version 1.0";
35
36#define MINIMUM_MAXIMUM_LINES 128
37
38/* If this particular system's header files define the macro `MAXPATHLEN',
39 we happily take advantage of it; otherwise we use a value which ought
40 to be large enough. */
41#ifndef MAXPATHLEN
42# define MAXPATHLEN 4096
43#endif
44#define NAME_TABLE_SIZE (MINIMUM_MAXIMUM_LINES * MAXPATHLEN)
45
48ac9ce2
AO
46#ifndef EXIT_SUCCESS
47# define EXIT_SUCCESS 0
48#endif
49#ifndef EXIT_FAILURE
50# define EXIT_FAILURE 1
51#endif
52
1f414ac4
BK
53char *file_name_buf;
54
0083c904
BK
55#define tSCC static const char
56#define tCC const char
57#define tSC static char
58
1f414ac4
BK
59typedef int t_success;
60
61#define FAILURE (-1)
62#define SUCCESS 0
63#define PROBLEM 1
0083c904 64
1f414ac4
BK
65#define SUCCEEDED(p) ((p) == SUCCESS)
66#define SUCCESSFUL(p) SUCCEEDED (p)
67#define FAILED(p) ((p) < SUCCESS)
68#define HADGLITCH(p) ((p) > SUCCESS)
0083c904 69
1f414ac4 70#define NUL '\0'
0083c904 71
1f414ac4 72/* Test Descriptor
0083c904 73
1f414ac4
BK
74 Each fix may have associated tests that determine
75 whether the fix needs to be applied or not.
76 Each test has a type (from the te_test_type enumeration);
77 associated test text; and, if the test is TT_EGREP or
78 the negated form TT_NEGREP, a pointer to the compiled
79 version of the text string.
80
81 */
0083c904 82typedef enum
1f414ac4
BK
83{
84 TT_TEST, TT_EGREP, TT_NEGREP
85} te_test_type;
0083c904
BK
86
87typedef struct test_desc tTestDesc;
88
89struct test_desc
1f414ac4
BK
90{
91 te_test_type type;
92 const char *pz_test_text;
93 regex_t *p_test_regex;
94};
0083c904
BK
95
96typedef struct patch_desc tPatchDesc;
97
1f414ac4
BK
98/* Fix Descriptor
99
100 Everything you ever wanted to know about how to apply
101 a particular fix (which files, how to qualify them,
102 how to actually make the fix, etc...)
103
104 */
0083c904
BK
105#define FD_MACH_ONLY 0x0000
106#define FD_MACH_IFNOT 0x0001
bb786201 107#define FD_SHELL_SCRIPT 0x0002
0083c904
BK
108#define FD_SKIP_TEST 0x8000
109
110typedef struct fix_desc tFixDesc;
111struct fix_desc
1f414ac4
BK
112{
113 const char* fix_name; /* Name of the fix */
114 const char* file_list; /* List of files it applies to */
115 const char** papz_machs; /* List of machine/os-es it applies to */
116 regex_t* unused;
117 int test_ct;
118 int fd_flags;
119 tTestDesc* p_test_desc;
120 const char** patch_args;
121};
122
123/* Working environment strings. Essentially, invocation 'options'. */
124char *pz_dest_dir = NULL;
125char *pz_src_dir = NULL;
126char *pz_machine = NULL;
7d9ccd90
BK
127char *pz_find_base = NULL;
128int find_base_len = 0;
1f414ac4
BK
129
130pid_t process_chain_head = (pid_t) -1;
131
132const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
133regex_t incl_quote_re;
134
94db2f71
BK
135char *load_file _P_((const char *));
136void process _P_((char *, const char *));
d1c6a037 137void run_compiles ();
94db2f71 138void wait_for_pid _P_(( pid_t ));
d1c6a037 139void initialize ();
0083c904
BK
140
141#include "fixincl.x"
142
1f414ac4
BK
143/* * * * * * * * * * * * * * * * * * *
144 *
145 * MAIN ROUTINE
146 */
0083c904
BK
147int
148main (argc, argv)
149 int argc;
150 char **argv;
151{
1f414ac4
BK
152 static const char gnu_lib_mark[] =
153 "This file is part of the GNU C Library";
0083c904
BK
154
155#ifndef NO_BOGOSITY_LIMITS
1f414ac4
BK
156# define BOGUS_LIMIT MINIMUM_MAXIMUM_LINES
157 size_t loop_ct;
0083c904
BK
158#endif
159
1f414ac4
BK
160 char *apz_names[BOGUS_LIMIT];
161 size_t file_name_ct;
0083c904 162
1f414ac4
BK
163 /* Before anything else, ensure we can allocate our file name buffer. */
164 file_name_buf = (char *) malloc (NAME_TABLE_SIZE);
165 if (file_name_buf == (char *) NULL)
0083c904 166 {
1f414ac4
BK
167 fprintf (stderr, "fixincl cannot allocate 0x%08X bytes\n",
168 NAME_TABLE_SIZE);
169 exit (EXIT_FAILURE);
170 }
0083c904 171
1f414ac4
BK
172 switch (argc)
173 {
174 case 1:
175 break;
176
177 case 2:
0083c904
BK
178 if (strcmp (argv[1], "-v") == 0)
179 {
1f414ac4
BK
180 static const char zFmt[] = "echo '%s'";
181
182 /* The 'version' option is really used to test that:
183 1. The program loads correctly (no missing libraries)
184 2. we can correctly run our server shell process
185 3. that we can compile all the regular expressions.
186 */
187 run_compiles ();
188 sprintf (file_name_buf, zFmt, program_id);
189 fputs (file_name_buf + 5, stdout);
190 exit (strcmp (run_shell (file_name_buf), program_id));
0083c904 191 }
0083c904 192 freopen (argv[1], "r", stdin);
1f414ac4
BK
193 break;
194
195 default:
196 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
197 exit (EXIT_FAILURE);
0083c904
BK
198 }
199
d1c6a037
BK
200 initialize ();
201
202#ifndef NO_BOGOSITY_LIMITS
203 /* Some systems only allow so many calls to fork(2).
204 This is inadequate for this program. Consequently,
205 we must let a grandfather process spawn children
206 that then spawn all the processes that do the real work.
207 */
208 for (;;)
209 {
210 file_name_ct = 0;
211
212 {
213 char *pz_buf = file_name_buf;
214
215 /* Only the parent process can read from stdin without confusing
216 the world. (How does the child tell the parent to skip
217 forward? Pipes and files behave differently.) */
218
219 while ( (file_name_ct < BOGUS_LIMIT)
220 && (pz_buf < (file_name_buf + NAME_TABLE_SIZE - MAXPATHLEN)))
221 {
222 if (fgets (pz_buf, MAXPATHLEN, stdin) == (char *) NULL)
223 break;
92a438d1 224 while (ISSPACE (*pz_buf))
d1c6a037
BK
225 pz_buf++;
226 if ((*pz_buf == '\0') || (*pz_buf == '#'))
227 continue;
228 apz_names[file_name_ct++] = pz_buf;
229 pz_buf += strlen (pz_buf);
92a438d1 230 while (ISSPACE (pz_buf[-1]))
d1c6a037
BK
231 pz_buf--;
232 *pz_buf++ = '\0';
233 }
234 }
235
236 /* IF we did not get any files this time thru
237 THEN we must be done. */
238 if (file_name_ct == 0)
239 return EXIT_SUCCESS;
240
94db2f71
BK
241 fflush (stdout);
242 fflush (stderr);
243
d1c6a037
BK
244 {
245 pid_t child = fork ();
246 if (child == NULLPROCESS)
247 break;
248
249 if (child == NOPROCESS)
250 {
251 fprintf (stderr, "Error %d (%s) forking in main\n",
252 errno, strerror (errno));
253 exit (EXIT_FAILURE);
254 }
255
94db2f71
BK
256#ifdef DEBUG
257 fprintf (stderr, "Waiting for %d to complete %d files\n",
258 child, file_name_ct);
259#endif
260
d1c6a037
BK
261 wait_for_pid( child, file_name_ct );
262 }
263 }
264#else
18001328 265 /*#*/ error "NON-BOGUS LIMITS NOT SUPPORTED?!?!"
d1c6a037
BK
266#endif
267
d1c6a037
BK
268 /* For every file specified in stdandard in
269 (except as throttled for bogus reasons)...
270 */
271 for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++)
272 {
273 char *pz_data;
274 char *pz_file_name = apz_names[loop_ct];
275
276 if (access (pz_file_name, R_OK) != 0)
277 {
278 int erno = errno;
279 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
280 pz_file_name, getcwd ((char *) NULL, MAXPATHLEN),
281 erno, strerror (erno));
282 }
283 else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL))
284 {
285 if (strstr (pz_data, gnu_lib_mark) == (char *) NULL)
286 process (pz_data, pz_file_name);
287 free ((void *) pz_data);
288 }
289 }
290
291 return EXIT_SUCCESS;
292}
293
294
295/* * * * * * * * * * * * */
296
297void
298initialize()
299{
300 static const char var_not_found[] =
301 "fixincl ERROR: %s environment variable not defined\n";
302
0083c904 303 {
1f414ac4
BK
304 static const char var[] = "TARGET_MACHINE";
305 pz_machine = getenv (var);
306 if (pz_machine == (char *) NULL)
0083c904 307 {
1f414ac4 308 fprintf (stderr, var_not_found, var);
0083c904
BK
309 exit (EXIT_FAILURE);
310 }
311 }
312
313 {
1f414ac4
BK
314 static const char var[] = "DESTDIR";
315 pz_dest_dir = getenv (var);
316 if (pz_dest_dir == (char *) NULL)
0083c904 317 {
1f414ac4 318 fprintf (stderr, var_not_found, var);
0083c904
BK
319 exit (EXIT_FAILURE);
320 }
321 }
322
323 {
1f414ac4
BK
324 static const char var[] = "SRCDIR";
325 pz_src_dir = getenv (var);
326 if (pz_src_dir == (char *) NULL)
0083c904 327 {
1f414ac4 328 fprintf (stderr, var_not_found, var);
0083c904
BK
329 exit (EXIT_FAILURE);
330 }
331 }
332
7d9ccd90
BK
333 {
334 static const char var[] = "FIND_BASE";
335 pz_find_base = getenv (var);
336 if (pz_find_base == (char *) NULL)
337 {
338 fprintf (stderr, var_not_found, var);
339 exit (EXIT_FAILURE);
340 }
341 find_base_len = strlen( pz_find_base );
342 }
343
1f414ac4
BK
344 /* Compile all the regular expressions now.
345 That way, it is done only once for the whole run.
346 */
347 run_compiles ();
0083c904 348
1f414ac4
BK
349 signal (SIGQUIT, SIG_IGN);
350 signal (SIGIOT, SIG_IGN);
351 signal (SIGPIPE, SIG_IGN);
352 signal (SIGALRM, SIG_IGN);
353 signal (SIGTERM, SIG_IGN);
0083c904 354
d1c6a037
BK
355 /*
356 Make sure that if we opened a server process, we close it now.
357 This is the grandparent process. We don't need the server anymore
358 and our children should make their own. */
0083c904 359
d1c6a037
BK
360 close_server ();
361 (void)wait ( (int*)NULL );
362}
0083c904 363
d1c6a037
BK
364/* * * * * * * * * * * * *
365
366 wait_for_pid - Keep calling `wait(2)' until it returns
367 the process id we are looking for. Not every system has
368 `waitpid(2)'. We also ensure that the children exit with success. */
7d9ccd90 369
d1c6a037 370void
48ac9ce2
AO
371wait_for_pid(child, file_name_ct)
372 pid_t child;
373 int file_name_ct;
d1c6a037 374{
d1c6a037
BK
375 for (;;) {
376 int status;
377 pid_t dead_kid = wait (&status);
7d9ccd90 378
d1c6a037
BK
379 if (dead_kid == child)
380 {
381 if (! WIFEXITED( status ))
382 {
383 fprintf (stderr, "child process %d is hung on signal %d\n",
384 child, WSTOPSIG( status ));
385 exit (EXIT_FAILURE);
386 }
387 if (WEXITSTATUS( status ) != 0)
388 {
389 fprintf (stderr, "child process %d exited with status %d\n",
390 child, WEXITSTATUS( status ));
391 exit (EXIT_FAILURE);
392 }
d1c6a037
BK
393 break; /* normal child completion */
394 }
1f414ac4 395
d1c6a037
BK
396 /*
397 IF there is an error, THEN see if it is retryable.
398 If it is not retryable, then break out of this loop. */
399 if (dead_kid == NOPROCESS)
400 {
401 switch (errno) {
402 case EINTR:
403 case EAGAIN:
404 break;
405
406 default:
407 fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
408 errno, strerror( errno ), child );
409 /* FALLTHROUGH */
410
411 case ECHILD: /* no children to wait for?? */
412 return;
0083c904 413 }
d1c6a037
BK
414 }
415 } done_waiting:;
0083c904
BK
416}
417
418
1f414ac4
BK
419/* * * * * * * * * * * * *
420
421 load_file loads all the contents of a file into malloc-ed memory.
422 Its argument is the name of the file to read in; the returned
423 result is the NUL terminated contents of the file. The file
424 is presumed to be an ASCII text file containing no NULs. */
0083c904 425char *
1f414ac4
BK
426load_file (pz_file_name)
427 const char *pz_file_name;
0083c904 428{
1f414ac4
BK
429 char *pz_data;
430 size_t file_size;
0083c904
BK
431
432 {
433 struct stat stbf;
1f414ac4
BK
434
435 if (stat (pz_file_name, &stbf) != 0)
0083c904
BK
436 {
437 fprintf (stderr, "error %d (%s) stat-ing %s\n",
1f414ac4 438 errno, strerror (errno), pz_file_name);
0083c904
BK
439 return (char *) NULL;
440 }
1f414ac4 441 file_size = stbf.st_size;
0083c904 442 }
1f414ac4 443 if (file_size == 0)
0083c904
BK
444 return (char *) NULL;
445
1f414ac4
BK
446 pz_data = (char *) malloc ((file_size + 16) & ~0x00F);
447 if (pz_data == (char *) NULL)
0083c904
BK
448 {
449 fprintf (stderr, "error: could not malloc %d bytes\n",
1f414ac4 450 file_size);
0083c904
BK
451 exit (EXIT_FAILURE);
452 }
453
454 {
1f414ac4
BK
455 FILE *fp = fopen (pz_file_name, "r");
456 size_t size_left = file_size;
457 char *read_ptr = pz_data;
0083c904
BK
458
459 if (fp == (FILE *) NULL)
460 {
461 fprintf (stderr, "error %d (%s) opening %s\n", errno,
1f414ac4
BK
462 strerror (errno), pz_file_name);
463 free ((void *) pz_data);
0083c904
BK
464 return (char *) NULL;
465 }
466
467 do
468 {
1f414ac4 469 size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp);
0083c904
BK
470
471 if (sizeRead == 0)
472 {
473 if (feof (fp))
474 break;
475
476 if (ferror (fp))
477 {
7d9ccd90
BK
478 int err = errno;
479 if (err != EISDIR)
480 fprintf (stderr, "error %d (%s) reading %s\n", err,
481 strerror (err), pz_file_name);
1f414ac4 482 free ((void *) pz_data);
0083c904
BK
483 fclose (fp);
484 return (char *) NULL;
485 }
486 }
487
1f414ac4
BK
488 read_ptr += sizeRead;
489 size_left -= sizeRead;
0083c904 490 }
1f414ac4 491 while (size_left != 0);
0083c904 492
1f414ac4 493 *read_ptr = '\0';
0083c904 494 fclose (fp);
0083c904 495 }
1f414ac4 496 return pz_data;
0083c904
BK
497}
498
499
1f414ac4
BK
500/* * * * * * * * * * * * *
501
502 run_compiles run all the regexp compiles for all the fixes once.
503 */
0083c904 504void
1f414ac4 505run_compiles ()
0083c904 506{
94db2f71
BK
507 tSCC z_bad_comp[] = "fixincl ERROR: cannot compile %s regex for %s\n\
508\texpr = `%s'\n\terror %s\n";
1f414ac4
BK
509 tFixDesc *p_fixd = fixDescList;
510 int fix_ct = FIX_COUNT;
511 tTestDesc *p_test;
512 int test_ct;
513 int re_ct = REGEX_COUNT;
514 const char *pz_err;
515 regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
516
517 if (p_re == (regex_t *) NULL)
0083c904
BK
518 {
519 fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n",
520 REGEX_COUNT * sizeof (regex_t));
521 exit (EXIT_FAILURE);
522 }
523
d1c6a037
BK
524 /* Make sure re_compile_pattern does not stumble across invalid
525 data */
526
527 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
528 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
529
1f414ac4
BK
530 /* The patterns we search for are all egrep patterns.
531 In the shell version of this program, we invoke egrep
532 with the supplied pattern. Here, we will run
533 re_compile_pattern, but it must be using the same rules. */
534
0083c904 535 re_set_syntax (RE_SYNTAX_EGREP);
1f414ac4
BK
536 pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
537 &incl_quote_re);
538 if (pz_err != (char *) NULL)
0083c904 539 {
1f414ac4
BK
540 fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
541 incl_quote_pat, pz_err);
0083c904
BK
542 exit (EXIT_FAILURE);
543 }
544
1f414ac4 545 /* FOR every fixup, ... */
0083c904
BK
546 do
547 {
1f414ac4
BK
548 p_test = p_fixd->p_test_desc;
549 test_ct = p_fixd->test_ct;
0083c904 550
1f414ac4
BK
551 /* IF the machine type pointer is not NULL (we are not in test mode)
552 AND this test is for or not done on particular machines
553 THEN ... */
554
555 if ( (pz_machine != NULL)
556 && (p_fixd->papz_machs != (const char**) NULL) )
557 {
558 const char **papz_machs = p_fixd->papz_machs;
559 char *pz = file_name_buf;
560 char *pz_sep = "";
561 tCC *pz_if_true;
562 tCC *pz_if_false;
563 tSCC skip[] = "skip";
564 tSCC run[] = "run";
565
566 /* Construct a shell script that looks like this:
567
568 case our-cpu-platform-os in
569 tests-cpu-platform-os-pattern )
570 echo run ;;
571 * )
572 echo skip ;;
573 esac
574
575 where 'run' and 'skip' may be reversed, depending on
576 the sense of the test. */
577
578 sprintf (pz, "case %s in\n", pz_machine);
579 pz += strlen (pz);
580
581 if (p_fixd->fd_flags & FD_MACH_IFNOT)
582 {
583 pz_if_true = skip;
584 pz_if_false = run;
585 }
586 else
587 {
588 pz_if_true = run;
589 pz_if_false = skip;
590 }
591
592 /* FOR any additional machine names to test for,
593 insert the " | \\\n" glue and the machine pattern. */
594
595 for (;;)
596 {
597 const char* pz_mach = *(papz_machs++);
598
599 if (pz_mach == (const char*) NULL)
600 break;
601 sprintf (pz, "%s %s", pz_sep, pz_mach);
602 pz += strlen (pz);
603 pz_sep = " | \\\n";
604 }
605 sprintf (pz, " )\n echo %s ;;\n * )\n echo %s ;;\nesac",
606 pz_if_true, pz_if_false);
607
608 /* Run the script.
609 The result will start either with 's' or 'r'. */
610
7d032a4f
RO
611 {
612 int skip;
613 pz = run_shell (file_name_buf);
614 skip = (*pz == 's');
615 free ( (void*)pz );
616 if (skip)
617 {
618 p_fixd->fd_flags |= FD_SKIP_TEST;
619 continue;
620 }
621 }
0083c904 622 }
0083c904 623
1f414ac4
BK
624 /* FOR every test for the fixup, ... */
625
626 while (--test_ct >= 0)
0083c904 627 {
1f414ac4 628 switch (p_test->type)
0083c904
BK
629 {
630 case TT_EGREP:
631 case TT_NEGREP:
1f414ac4
BK
632 /* You might consider putting the following under #ifdef.
633 The number of re's used is computed by autogen.
634 So, it is static and known at compile time. */
635
636 if (--re_ct < 0)
0083c904
BK
637 {
638 fputs ("out of RE's\n", stderr);
639 exit (EXIT_FAILURE);
640 }
641
1f414ac4
BK
642 p_test->p_test_regex = p_re++;
643 pz_err = re_compile_pattern (p_test->pz_test_text,
644 strlen (p_test->pz_test_text),
645 p_test->p_test_regex);
646 if (pz_err != (char *) NULL)
0083c904 647 {
1f414ac4
BK
648 fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name,
649 p_test->pz_test_text, pz_err);
0083c904
BK
650 exit (EXIT_FAILURE);
651 }
652 }
1f414ac4 653 p_test++;
0083c904
BK
654 }
655 }
1f414ac4 656 while (p_fixd++, --fix_ct > 0);
0083c904
BK
657}
658
659
1f414ac4
BK
660/* * * * * * * * * * * * *
661
662 create_file Create the output modified file.
663 Input: the name of the file to create
664 Returns: a file pointer to the new, open file */
665
666#define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
667
0083c904 668FILE *
1f414ac4
BK
669create_file (pz_file_name)
670 const char *pz_file_name;
0083c904
BK
671{
672 int fd;
673 FILE *pf;
674 char fname[MAXPATHLEN];
675
7d9ccd90
BK
676#ifdef DEBUG
677 if (strncmp( pz_file_name, pz_find_base, find_base_len ) != 0)
678 {
679 fprintf (stderr, "Error: input file %s does not match %s/*\n",
680 pz_file_name, pz_find_base );
681 exit (1);
682 }
683#endif
684
685 sprintf (fname, "%s/%s", pz_dest_dir, pz_file_name + find_base_len);
0083c904 686
1f414ac4 687 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
0083c904 688
1f414ac4 689 /* We may need to create the directories needed... */
0083c904
BK
690 if ((fd < 0) && (errno == ENOENT))
691 {
1f414ac4 692 char *pz_dir = strchr (fname + 1, '/');
0083c904
BK
693 struct stat stbf;
694
1f414ac4 695 while (pz_dir != (char *) NULL)
0083c904 696 {
1f414ac4 697 *pz_dir = NUL;
0083c904
BK
698 if (stat (fname, &stbf) < 0)
699 {
700 mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
701 | S_IROTH | S_IXOTH);
702 }
703
1f414ac4
BK
704 *pz_dir = '/';
705 pz_dir = strchr (pz_dir + 1, '/');
0083c904 706 }
1f414ac4
BK
707
708 /* Now, lets try the open again... */
709 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
0083c904
BK
710 }
711 if (fd < 0)
712 {
713 fprintf (stderr, "Error %d (%s) creating %s\n",
714 errno, strerror (errno), fname);
715 exit (EXIT_FAILURE);
716 }
1f414ac4 717 fprintf (stderr, "Fixed: %s\n", pz_file_name);
0083c904
BK
718 pf = fdopen (fd, "w");
719
720#ifdef LATER
721 {
1f414ac4
BK
722 static const char hdr[] =
723 "/* DO NOT EDIT THIS FILE.\n\n"
724 " It has been auto-edited by fixincludes from /usr/include/%s\n"
725 " This had to be done to correct non-standard usages in the\n"
726 " original, manufacturer supplied header file. */\n\n";
727
728 fprintf (pf, hdr, pz_file_name);
0083c904
BK
729 }
730#endif
731 return pf;
732}
733
0083c904 734
1f414ac4 735/* * * * * * * * * * * * *
0083c904 736
1f414ac4
BK
737 test_test make sure a shell-style test expression passes.
738 Input: a pointer to the descriptor of the test to run and
739 the name of the file that we might want to fix
740 Result: SUCCESS or FAILURE, depending on the result of the
741 shell script we run. */
742
743t_success
744test_test (p_test, pz_file_name)
745 tTestDesc *p_test;
746 char* pz_file_name;
747{
94db2f71
BK
748 tSCC cmd_fmt[] =
749"file=%s\n\
750if ( test %s ) > /dev/null 2>&1\n\
751then echo TRUE\n\
752else echo FALSE\n\
753fi";
754
1f414ac4
BK
755 char *pz_res;
756 t_success res = FAILURE;
757
758 static char cmd_buf[4096];
1f414ac4
BK
759
760 sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text);
761 pz_res = run_shell (cmd_buf);
762 if (*pz_res == 'T')
0083c904 763 res = SUCCESS;
1f414ac4 764 free ((void *) pz_res);
0083c904
BK
765 return res;
766}
767
768
1f414ac4
BK
769/* * * * * * * * * * * * *
770
771 egrep_test make sure an egrep expression is found in the file text.
772 Input: a pointer to the descriptor of the test to run and
773 the pointer to the contents of the file under suspicion
774 Result: SUCCESS if the pattern is found, FAILURE otherwise
775
776 The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test
777 is inverted. */
778
779t_success
780egrep_test (pz_data, p_test)
781 char *pz_data;
782 tTestDesc *p_test;
0083c904
BK
783{
784 regmatch_t match;
1f414ac4 785
0083c904 786#ifndef NO_BOGOSITY
1f414ac4
BK
787 if (p_test->p_test_regex == 0)
788 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
789 p_test->pz_test_text);
0083c904 790#endif
1f414ac4 791 if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
0083c904
BK
792 return SUCCESS;
793 return FAILURE;
794}
795
796
94db2f71
BK
797/* * * * * * * * * * * * *
798
799 quoted_file_exists Make sure that a file exists before we emit
800 the file name. If we emit the name, our invoking shell will try
801 to copy a non-existing file into the destination directory. */
802
803int
804quoted_file_exists (pz_src_path, pz_file_path, pz_file)
805 char* pz_src_path;
806 char* pz_file_path;
807 char* pz_file;
808{
809 char z[ MAXPATHLEN ];
810 char* pz;
811 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
812 pz = z + strlen ( z );
813
814 for (;;) {
815 char ch = *pz_file++;
92a438d1 816 if (! ISGRAPH(ch))
94db2f71
BK
817 return 0;
818 if (ch == '"')
819 break;
820 *pz++ = ch;
821 }
822 *pz = '\0';
823 {
824 struct stat s;
825 if (stat (z, &s) != 0)
826 return 0;
827 return S_ISREG( s.st_mode );
828 }
829}
830
831
1f414ac4
BK
832/* * * * * * * * * * * * *
833 *
834 extract_quoted_files
835
836 The syntax, `#include "file.h"' specifies that the compiler is to
837 search the local directory of the current file before the include
838 list. Consequently, if we have modified a header and stored it in
839 another directory, any files that are included by that modified
840 file in that fashion must also be copied into this new directory.
841 This routine finds those flavors of #include and for each one found
842 emits a triple of:
843
844 1. source directory of the original file
845 2. the relative path file name of the #includ-ed file
846 3. the full destination path for this file
847
848 Input: the text of the file, the file name and a pointer to the
849 match list where the match information was stored.
850 Result: internally nothing. The results are written to stdout
851 for interpretation by the invoking shell */
0083c904 852
94db2f71 853
0083c904 854void
1f414ac4
BK
855extract_quoted_files (pz_data, pz_file_name, p_re_match)
856 char *pz_data;
857 const char *pz_file_name;
858 regmatch_t *p_re_match;
0083c904 859{
1f414ac4
BK
860 char *pz_dir_end = strrchr (pz_file_name, '/');
861 char *pz_incl_quot = pz_data;
0083c904 862
1f414ac4 863 fprintf (stderr, "Quoted includes in %s\n", pz_file_name);
0083c904 864
1f414ac4 865 /* Set "pz_file_name" to point to the containing subdirectory of the source
94db2f71 866 If there is none, then it is in our current directory, ".". */
1f414ac4
BK
867
868 if (pz_dir_end == (char *) NULL)
869 pz_file_name = ".";
0083c904 870 else
1f414ac4 871 *pz_dir_end = '\0';
0083c904
BK
872
873 for (;;)
874 {
1f414ac4
BK
875 pz_incl_quot += p_re_match->rm_so;
876
877 /* Skip forward to the included file name */
92a438d1 878 while (ISSPACE (*pz_incl_quot))
1f414ac4 879 pz_incl_quot++;
92a438d1
KG
880 /* ISSPACE() may evaluate is argument more than once! */
881 while ((++pz_incl_quot, ISSPACE (*pz_incl_quot)))
1f414ac4
BK
882 ;
883 pz_incl_quot += sizeof ("include") - 1;
884 while (*pz_incl_quot++ != '"')
885 ;
886
94db2f71
BK
887 if (quoted_file_exists (pz_src_dir, pz_file_name, pz_incl_quot))
888 {
889 /* Print the source directory and the subdirectory
890 of the file in question. */
891 printf ("%s %s/", pz_src_dir, pz_file_name);
892 pz_dir_end = pz_incl_quot;
893
894 /* Append to the directory the relative path of the desired file */
895 while (*pz_incl_quot != '"')
896 putc (*pz_incl_quot++, stdout);
897
898 /* Now print the destination directory appended with the
899 relative path of the desired file */
900 printf (" %s/%s/", pz_dest_dir, pz_file_name);
901 while (*pz_dir_end != '"')
902 putc (*pz_dir_end++, stdout);
903
904 /* End of entry */
905 putc ('\n', stdout);
906 }
0083c904 907
1f414ac4
BK
908 /* Find the next entry */
909 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
0083c904
BK
910 break;
911 }
912}
913
914
bb786201
BK
915
916/* * * * * * * * * * * * *
917
918 This loop should only cycle for 1/2 of one loop.
919 "chain_open" starts a process that uses "read_fd" as
920 its stdin and returns the new fd this process will use
921 for stdout. */
922
923int
924start_fixer (read_fd, p_fixd, pz_file_name)
925 int read_fd;
926 tFixDesc* p_fixd;
927 char* pz_file_name;
928{
929 tSCC z_err[] = "Error %d (%s) starting filter process for %s\n";
930 tCC* pz_cmd_save;
931 char* pz_cmd;
932
933 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
934 pz_cmd = (char*)NULL;
935 else
936 {
937 tSCC z_cmd_fmt[] = "file='%s'\n%s";
938 pz_cmd = (char*)xmalloc (strlen (p_fixd->patch_args[2])
939 + sizeof( z_cmd_fmt )
940 + strlen( pz_file_name ));
941 sprintf (pz_cmd, z_cmd_fmt, pz_file_name, p_fixd->patch_args[2]);
942 pz_cmd_save = p_fixd->patch_args[2];
943 p_fixd->patch_args[2] = pz_cmd;
944 }
945
946 for (;;)
947 {
948 static int failCt = 0;
949 int fd;
950
951 fd = chain_open (read_fd,
952 (t_pchar *) p_fixd->patch_args,
953 (process_chain_head == -1)
954 ? &process_chain_head : (pid_t *) NULL);
955
956 if (fd != -1)
957 {
958 read_fd = fd;
959 break;
960 }
961
962 fprintf (stderr, z_err, errno, strerror (errno),
963 p_fixd->fix_name);
964
965 if ((errno != EAGAIN) || (++failCt > 10))
966 exit (EXIT_FAILURE);
967 sleep (1);
968 }
969
970 if (pz_cmd != (char*)NULL)
971 {
972 free ((void*)pz_cmd);
973 p_fixd->patch_args[2] = pz_cmd_save;
974 }
975
976 return read_fd;
977}
978
1f414ac4
BK
979/* * * * * * * * * * * * *
980
981 Process the potential fixes for a particular include file.
982 Input: the original text of the file and the file's name
983 Result: none. A new file may or may not be created. */
984
0083c904 985void
1f414ac4
BK
986process (pz_data, pz_file_name)
987 char *pz_data;
988 const char *pz_file_name;
0083c904 989{
48ac9ce2 990 static char env_current_file[1024];
1f414ac4
BK
991 tFixDesc *p_fixd = fixDescList;
992 int todo_ct = FIX_COUNT;
48ac9ce2 993 int read_fd = -1;
9f8eec39 994 int num_children = 0;
0083c904 995
1f414ac4 996 process_chain_head = NOPROCESS;
94db2f71 997 fprintf (stderr, "%-50s \r", pz_file_name );
1f414ac4
BK
998 /* For every fix in our fix list, ... */
999 for (; todo_ct > 0; p_fixd++, todo_ct--)
0083c904 1000 {
1f414ac4
BK
1001 tTestDesc *p_test;
1002 int test_ct;
0083c904 1003
1f414ac4 1004 if (p_fixd->fd_flags & FD_SKIP_TEST)
0083c904
BK
1005 continue;
1006
1f414ac4
BK
1007 /* IF there is a file name restriction,
1008 THEN ensure the current file name matches one in the pattern */
1009
1010 if (p_fixd->file_list != (char *) NULL)
0083c904 1011 {
1f414ac4
BK
1012 const char *pz_fname = pz_file_name;
1013 const char *pz_scan = p_fixd->file_list;
1014 size_t name_len;
0083c904 1015
1f414ac4
BK
1016 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1017 pz_fname += 2;
1018 name_len = strlen (pz_fname);
0083c904
BK
1019
1020 for (;;)
1021 {
1f414ac4
BK
1022 pz_scan = strstr (pz_scan + 1, pz_fname);
1023 /* IF we can't match the string at all,
1024 THEN bail */
1025 if (pz_scan == (char *) NULL)
1026 goto next_fix;
0083c904 1027
1f414ac4
BK
1028 /* IF the match is surrounded by the '|' markers,
1029 THEN we found a full match -- time to run the tests */
1030
1031 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
0083c904
BK
1032 break;
1033 }
1034 }
1035
1f414ac4
BK
1036 /* FOR each test, see if it fails.
1037 IF it does fail, then we go on to the next test */
0083c904 1038
1f414ac4
BK
1039 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1040 test_ct-- > 0;
1041 p_test++)
0083c904 1042 {
94db2f71 1043#ifdef DEBUG_TEST
1f414ac4
BK
1044 static const char z_test_fail[] =
1045 "%16s test %2d failed for %s\n";
1046#endif
1047 switch (p_test->type)
0083c904
BK
1048 {
1049 case TT_TEST:
1f414ac4
BK
1050 if (!SUCCESSFUL (test_test (p_test, pz_file_name)))
1051 {
94db2f71 1052#ifdef DEBUG_TEST
1f414ac4
BK
1053 fprintf (stderr, z_test_fail, p_fixd->fix_name,
1054 p_fixd->test_ct - test_ct, pz_file_name);
1055#endif
1056 goto next_fix;
1057 }
0083c904
BK
1058 break;
1059
1060 case TT_EGREP:
1f414ac4
BK
1061 if (!SUCCESSFUL (egrep_test (pz_data, p_test)))
1062 {
94db2f71 1063#ifdef DEBUG_TEST
1f414ac4
BK
1064 fprintf (stderr, z_test_fail, p_fixd->fix_name,
1065 p_fixd->test_ct - test_ct, pz_file_name);
1066#endif
1067 goto next_fix;
1068 }
0083c904
BK
1069 break;
1070
1071 case TT_NEGREP:
1f414ac4
BK
1072 if (SUCCESSFUL (egrep_test (pz_data, p_test)))
1073 {
94db2f71 1074#ifdef DEBUG_TEST
1f414ac4
BK
1075 fprintf (stderr, z_test_fail, p_fixd->fix_name,
1076 p_fixd->test_ct - test_ct, pz_file_name);
1077#endif
1078 goto next_fix;
1079 }
0083c904
BK
1080 break;
1081 }
1082 }
1083
7d9ccd90 1084 fprintf (stderr, "Applying %-24s to %s\n",
1f414ac4
BK
1085 p_fixd->fix_name, pz_file_name);
1086
1087 /* IF we do not have a read pointer,
1088 THEN this is the first fix for the current file.
1089 Open the source file. That will be used as stdin for
1090 the first fix. Any subsequent fixes will use the
1091 stdout descriptor of the previous fix as its stdin. */
0083c904 1092
48ac9ce2 1093 if (read_fd == -1)
1f414ac4 1094 {
48ac9ce2
AO
1095 read_fd = open (pz_file_name, O_RDONLY);
1096 if (read_fd < 0)
0083c904 1097 {
1f414ac4
BK
1098 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1099 strerror (errno), pz_file_name);
1100 exit (EXIT_FAILURE);
0083c904 1101 }
1f414ac4 1102 }
0083c904 1103
bb786201
BK
1104 read_fd = start_fixer (read_fd, p_fixd, pz_file_name);
1105 num_children++;
0083c904 1106
1f414ac4
BK
1107 next_fix:
1108 ;
0083c904
BK
1109 }
1110
1f414ac4
BK
1111 /* IF after all the tests we did not start any patch programs,
1112 THEN quit now. */
1113
48ac9ce2 1114 if (read_fd < 0)
0083c904
BK
1115 return;
1116
1f414ac4
BK
1117 /* OK. We have work to do. Read back in the output
1118 of the filtering chain. Compare each byte as we read it with
1119 the contents of the original file. As soon as we find any
1120 difference, we will create the output file, write out all
1121 the matched text and then copy any remaining data from the
1122 output of the filter chain.
1123 */
0083c904 1124 {
48ac9ce2 1125 FILE *in_fp = fdopen (read_fd, "r");
1f414ac4
BK
1126 FILE *out_fp = (FILE *) NULL;
1127 char *pz_cmp = pz_data;
0083c904
BK
1128
1129 for (;;)
1130 {
1131 int ch;
1132
1f414ac4 1133 ch = getc (in_fp);
0083c904
BK
1134 if (ch == EOF)
1135 break;
1136
1f414ac4
BK
1137 /* IF we are emitting the output
1138 THEN emit this character, too.
1139 */
1140 if (out_fp != (FILE *) NULL)
1141 putc (ch, out_fp);
0083c904 1142
1f414ac4
BK
1143 /* ELSE if this character does not match the original,
1144 THEN now is the time to start the output.
1145 */
1146 else if (ch != *pz_cmp)
0083c904 1147 {
1f414ac4
BK
1148 out_fp = create_file (pz_file_name);
1149
1150 /* IF there are matched data, write it all now. */
1151 if (pz_cmp != pz_data)
0083c904 1152 {
1f414ac4
BK
1153 char c = *pz_cmp;
1154
1155 *pz_cmp = NUL;
1156 fputs (pz_data, out_fp);
1157 *pz_cmp = c;
0083c904 1158 }
0083c904 1159
1f414ac4
BK
1160 /* Emit the current unmatching character */
1161 putc (ch, out_fp);
0083c904
BK
1162 }
1163 else
1f414ac4
BK
1164 /* ELSE the character matches. Advance the compare ptr */
1165 pz_cmp++;
0083c904
BK
1166 }
1167
1f414ac4
BK
1168 /* IF we created the output file, ... */
1169 if (out_fp != (FILE *) NULL)
0083c904
BK
1170 {
1171 regmatch_t match;
1172
1f414ac4
BK
1173 /* Close the file and see if we have to worry about
1174 `#include "file.h"' constructs. */
1175 fclose (out_fp);
1176 if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0)
1177 extract_quoted_files (pz_data, pz_file_name, &match);
0083c904 1178 }
1f414ac4 1179 fclose (in_fp);
0083c904 1180 }
48ac9ce2 1181 close (read_fd); /* probably redundant, but I'm paranoid */
9f8eec39
RO
1182
1183 /* Wait for child processes created by chain_open()
1184 to avoid creating zombies. */
1185 while (--num_children >= 0)
1186 wait ((int *) NULL);
0083c904 1187}