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