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