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