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