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