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