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