2011-01-14 Tobias Burnus <burnus@net-b.de>
[official-gcc.git] / fixincludes / fixincl.c
blob9f399abdd83d54660726a8f44eb5c8fcfcaf8149
1 /* Install modified versions of certain ANSI-incompatible system header
2 files which are fixed to work correctly with ANSI C and placed in a
3 directory that GCC will search.
5 Copyright (C) 1997, 1998, 1999, 2000, 2004, 2009
6 Free Software Foundation, Inc.
8 This file is part of GCC.
10 GCC 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 3, or (at your option)
13 any later version.
15 GCC 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.
20 You should have received a copy of the GNU General Public License
21 along with GCC; see the file COPYING3. If not see
22 <http://www.gnu.org/licenses/>. */
24 #include "fixlib.h"
26 #include <fnmatch.h>
27 #include <sys/stat.h>
28 #ifndef SEPARATE_FIX_PROC
29 #include <sys/wait.h>
30 #endif
32 #if defined( HAVE_MMAP_FILE )
33 #include <sys/mman.h>
34 #define BAD_ADDR ((void*)-1)
35 #endif
37 #ifndef SEPARATE_FIX_PROC
38 #include "server.h"
39 #endif
41 /* The contents of this string are not very important. It is mostly
42 just used as part of the "I am alive and working" test. */
44 static const char program_id[] = "fixincl version 1.1";
46 /* This format will be used at the start of every generated file */
48 static const char z_std_preamble[] =
49 "/* DO NOT EDIT THIS FILE.\n\n\
50 It has been auto-edited by fixincludes from:\n\n\
51 \t\"%s/%s\"\n\n\
52 This had to be done to correct non-standard usages in the\n\
53 original, manufacturer supplied header file. */\n\n";
55 int find_base_len = 0;
57 typedef enum {
58 VERB_SILENT = 0,
59 VERB_FIXES,
60 VERB_APPLIES,
61 VERB_PROGRESS,
62 VERB_TESTS,
63 VERB_EVERYTHING
64 } te_verbose;
66 te_verbose verbose_level = VERB_PROGRESS;
67 int have_tty = 0;
69 #define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
70 #define NOT_SILENT VLEVEL(VERB_FIXES)
72 pid_t process_chain_head = (pid_t) -1;
74 char* pz_curr_file; /* name of the current file under test/fix */
75 char* pz_curr_data; /* original contents of that file */
76 char* pz_temp_file; /* for DOS, a place to stash the temporary
77 fixed data between system(3) calls */
78 t_bool curr_data_mapped;
79 int data_map_fd;
80 size_t data_map_size;
81 size_t ttl_data_size = 0;
83 #ifdef DO_STATS
84 int process_ct = 0;
85 int apply_ct = 0;
86 int fixed_ct = 0;
87 int altered_ct = 0;
88 #endif /* DO_STATS */
90 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
91 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
92 regex_t incl_quote_re;
94 static void do_version (void) ATTRIBUTE_NORETURN;
95 char *load_file (const char *);
96 void run_compiles (void);
97 void initialize (int argc, char** argv);
98 void process (void);
100 /* External Source Code */
102 #include "fixincl.x"
104 /* * * * * * * * * * * * * * * * * * *
106 * MAIN ROUTINE
108 extern int main (int, char **);
110 main (int argc, char** argv)
112 char *file_name_buf;
114 initialize ( argc, argv );
116 have_tty = isatty (fileno (stderr));
118 /* Before anything else, ensure we can allocate our file name buffer. */
119 file_name_buf = load_file_data (stdin);
121 /* Because of the way server shells work, you have to keep stdin, out
122 and err open so that the proper input file does not get closed
123 by accident */
125 freopen ("/dev/null", "r", stdin);
127 if (file_name_buf == (char *) NULL)
129 fputs ("No file names listed for fixing\n", stderr);
130 exit (EXIT_FAILURE);
133 for (;;)
135 char* pz_end;
137 /* skip to start of name, past any "./" prefixes */
139 while (ISSPACE (*file_name_buf)) file_name_buf++;
140 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
141 file_name_buf += 2;
143 /* Check for end of list */
145 if (*file_name_buf == NUL)
146 break;
148 /* Set global file name pointer and find end of name */
150 pz_curr_file = file_name_buf;
151 pz_end = strchr( pz_curr_file, '\n' );
152 if (pz_end == (char*)NULL)
153 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
154 else
155 file_name_buf = pz_end + 1;
157 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
159 /* IF no name is found (blank line) or comment marker, skip line */
161 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
162 continue;
163 *pz_end = NUL;
165 process ();
166 } /* for (;;) */
168 #ifdef DO_STATS
169 if (VLEVEL( VERB_PROGRESS )) {
170 tSCC zFmt[] =
172 Processed %5d files containing %d bytes \n\
173 Applying %5d fixes to %d files\n\
174 Altering %5d of them\n";
176 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
177 fixed_ct, altered_ct);
179 #endif /* DO_STATS */
181 # ifdef SEPARATE_FIX_PROC
182 unlink( pz_temp_file );
183 # endif
184 exit (EXIT_SUCCESS);
188 static void
189 do_version (void)
191 static const char zFmt[] = "echo '%s'";
192 char zBuf[ 1024 ];
194 /* The 'version' option is really used to test that:
195 1. The program loads correctly (no missing libraries)
196 2. that we can compile all the regular expressions.
197 3. we can correctly run our server shell process
199 run_compiles ();
200 sprintf (zBuf, zFmt, program_id);
201 #ifndef SEPARATE_FIX_PROC
202 puts (zBuf + 5);
203 exit (strcmp (run_shell (zBuf), program_id));
204 #else
205 exit (system (zBuf));
206 #endif
209 /* * * * * * * * * * * * */
211 void
212 initialize ( int argc, char** argv )
214 xmalloc_set_program_name (argv[0]);
216 switch (argc)
218 case 1:
219 break;
221 case 2:
222 if (strcmp (argv[1], "-v") == 0)
223 do_version ();
224 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
226 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
227 errno, xstrerror (errno), argv[1] );
228 exit (EXIT_FAILURE);
230 break;
232 default:
233 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
234 exit (EXIT_FAILURE);
237 #ifdef SIGCHLD
238 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
239 receive the signal. A different setting is inheritable */
240 signal (SIGCHLD, SIG_DFL);
241 #endif
243 initialize_opts ();
245 if (ISDIGIT ( *pz_verbose ))
246 verbose_level = (te_verbose)atoi( pz_verbose );
247 else
248 switch (*pz_verbose) {
249 case 's':
250 case 'S':
251 verbose_level = VERB_SILENT; break;
253 case 'f':
254 case 'F':
255 verbose_level = VERB_FIXES; break;
257 case 'a':
258 case 'A':
259 verbose_level = VERB_APPLIES; break;
261 default:
262 case 'p':
263 case 'P':
264 verbose_level = VERB_PROGRESS; break;
266 case 't':
267 case 'T':
268 verbose_level = VERB_TESTS; break;
270 case 'e':
271 case 'E':
272 verbose_level = VERB_EVERYTHING; break;
274 if (verbose_level >= VERB_EVERYTHING) {
275 verbose_level = VERB_EVERYTHING;
276 fputs ("fixinc verbosity: EVERYTHING\n", stderr);
278 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
279 pz_find_base += 2;
280 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
281 find_base_len = strlen( pz_find_base );
283 /* Compile all the regular expressions now.
284 That way, it is done only once for the whole run.
286 run_compiles ();
288 # ifdef SEPARATE_FIX_PROC
289 /* NULL as the first argument to `tempnam' causes it to DTRT
290 wrt the temporary directory where the file will be created. */
291 pz_temp_file = tempnam( NULL, "fxinc" );
292 # endif
294 signal (SIGQUIT, SIG_IGN);
295 signal (SIGIOT, SIG_IGN);
296 signal (SIGPIPE, SIG_IGN);
297 signal (SIGALRM, SIG_IGN);
298 signal (SIGTERM, SIG_IGN);
301 /* * * * * * * * * * * * *
303 load_file loads all the contents of a file into malloc-ed memory.
304 Its argument is the name of the file to read in; the returned
305 result is the NUL terminated contents of the file. The file
306 is presumed to be an ASCII text file containing no NULs. */
307 char *
308 load_file ( const char* fname )
310 struct stat stbf;
311 char* res;
313 if (stat (fname, &stbf) != 0)
315 if (NOT_SILENT)
316 fprintf (stderr, "error %d (%s) stat-ing %s\n",
317 errno, xstrerror (errno), fname );
318 return (char *) NULL;
320 if (stbf.st_size == 0)
321 return (char*)NULL;
323 /* Make the data map size one larger than the file size for documentation
324 purposes. Truth is that there will be a following NUL character if
325 the file size is not a multiple of the page size. If it is a multiple,
326 then this adjustment sometimes fails anyway. */
327 data_map_size = stbf.st_size+1;
328 data_map_fd = open (fname, O_RDONLY);
329 ttl_data_size += data_map_size-1;
331 if (data_map_fd < 0)
333 if (NOT_SILENT)
334 fprintf (stderr, "error %d (%s) opening %s for read\n",
335 errno, xstrerror (errno), fname);
336 return (char*)NULL;
339 #ifdef HAVE_MMAP_FILE
340 curr_data_mapped = BOOL_TRUE;
342 /* IF the file size is a multiple of the page size,
343 THEN sometimes you will seg fault trying to access a trailing byte */
344 if ((stbf.st_size & (getpagesize()-1)) == 0)
345 res = (char*)BAD_ADDR;
346 else
347 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
348 MAP_PRIVATE, data_map_fd, 0);
349 if (res == (char*)BAD_ADDR)
350 #endif
352 FILE* fp = fdopen (data_map_fd, "r");
353 curr_data_mapped = BOOL_FALSE;
354 res = load_file_data (fp);
355 fclose (fp);
358 return res;
361 static int
362 machine_matches( tFixDesc* p_fixd )
364 char const ** papz_machs = p_fixd->papz_machs;
365 int have_match = BOOL_FALSE;
367 for (;;)
369 char const * pz_mpat = *(papz_machs++);
370 if (pz_mpat == NULL)
371 break;
372 if (fnmatch(pz_mpat, pz_machine, 0) == 0)
374 have_match = BOOL_TRUE;
375 break;
379 /* Check for sense inversion then set the "skip test" flag, if needed */
380 if (p_fixd->fd_flags & FD_MACH_IFNOT)
381 have_match = ! have_match;
383 if (! have_match)
384 p_fixd->fd_flags |= FD_SKIP_TEST;
386 return have_match;
389 /* * * * * * * * * * * * *
391 * run_compiles run all the regexp compiles for all the fixes once.
393 void
394 run_compiles (void)
396 tFixDesc *p_fixd = fixDescList;
397 int fix_ct = FIX_COUNT;
398 regex_t *p_re = XCNEWVEC (regex_t, REGEX_COUNT);
400 /* Make sure compile_re does not stumble across invalid data */
402 memset (&incl_quote_re, '\0', sizeof (regex_t));
404 compile_re (incl_quote_pat, &incl_quote_re, 1,
405 "quoted include", "run_compiles");
407 /* Allow machine name tests to be ignored (testing, mainly) */
409 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
410 pz_machine = (char*)NULL;
412 /* FOR every fixup, ... */
415 tTestDesc *p_test = p_fixd->p_test_desc;
416 int test_ct = p_fixd->test_ct;
418 /* IF the machine type pointer is not NULL (we are not in test mode)
419 AND this test is for or not done on particular machines
420 THEN ... */
422 if ( (pz_machine != NULL)
423 && (p_fixd->papz_machs != (const char**) NULL)
424 && ! machine_matches (p_fixd) )
425 continue;
427 /* FOR every test for the fixup, ... */
429 while (--test_ct >= 0)
431 switch (p_test->type)
433 case TT_EGREP:
434 case TT_NEGREP:
435 p_test->p_test_regex = p_re++;
436 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
437 "select test", p_fixd->fix_name);
438 default: break;
440 p_test++;
443 while (p_fixd++, --fix_ct > 0);
447 /* * * * * * * * * * * * *
449 create_file Create the output modified file.
450 Input: the name of the file to create
451 Returns: a file pointer to the new, open file */
453 #if defined(S_IRUSR) && defined(S_IWUSR) && \
454 defined(S_IRGRP) && defined(S_IROTH)
456 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
457 #else
458 # define S_IRALL 0644
459 #endif
461 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
462 defined(S_IROTH) && defined(S_IXOTH)
464 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
465 #else
466 # define S_DIRALL 0755
467 #endif
470 static FILE *
471 create_file (void)
473 int fd;
474 FILE *pf;
475 char fname[MAXPATHLEN];
477 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
479 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
481 /* We may need to create the directories needed... */
482 if ((fd < 0) && (errno == ENOENT))
484 char *pz_dir = strchr (fname + 1, '/');
485 struct stat stbf;
487 while (pz_dir != (char *) NULL)
489 *pz_dir = NUL;
490 if (stat (fname, &stbf) < 0)
492 #ifdef _WIN32
493 mkdir (fname);
494 #else
495 mkdir (fname, S_IFDIR | S_DIRALL);
496 #endif
499 *pz_dir = '/';
500 pz_dir = strchr (pz_dir + 1, '/');
503 /* Now, lets try the open again... */
504 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
506 if (fd < 0)
508 fprintf (stderr, "Error %d (%s) creating %s\n",
509 errno, xstrerror (errno), fname);
510 exit (EXIT_FAILURE);
512 if (NOT_SILENT)
513 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
514 pf = fdopen (fd, "w");
517 * IF pz_machine is NULL, then we are in some sort of test mode.
518 * Do not insert the current directory name. Use a constant string.
520 fprintf (pf, z_std_preamble,
521 (pz_machine == NULL)
522 ? "fixinc/tests/inc"
523 : pz_input_dir,
524 pz_curr_file);
526 return pf;
530 /* * * * * * * * * * * * *
532 test_test make sure a shell-style test expression passes.
533 Input: a pointer to the descriptor of the test to run and
534 the name of the file that we might want to fix
535 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
536 shell script we run. */
537 #ifndef SEPARATE_FIX_PROC
538 static int
539 test_test (tTestDesc* p_test, char* pz_test_file)
541 tSCC cmd_fmt[] =
542 "file=%s\n\
543 if ( test %s ) > /dev/null 2>&1\n\
544 then echo TRUE\n\
545 else echo FALSE\n\
546 fi";
548 char *pz_res;
549 int res;
551 static char cmd_buf[4096];
553 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
554 pz_res = run_shell (cmd_buf);
556 switch (*pz_res) {
557 case 'T':
558 res = APPLY_FIX;
559 break;
561 case 'F':
562 res = SKIP_FIX;
563 break;
565 default:
566 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
567 pz_res, cmd_buf );
568 res = SKIP_FIX;
571 free ((void *) pz_res);
572 return res;
574 #else
576 * IF we are in MS-DOS land, then whatever shell-type test is required
577 * will, by definition, fail
579 #define test_test(t,tf) SKIP_FIX
580 #endif
582 /* * * * * * * * * * * * *
584 egrep_test make sure an egrep expression is found in the file text.
585 Input: a pointer to the descriptor of the test to run and
586 the pointer to the contents of the file under suspicion
587 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
589 The caller may choose to reverse meaning if the sense of the test
590 is inverted. */
592 static int
593 egrep_test (char* pz_data, tTestDesc* p_test)
595 #ifdef DEBUG
596 if (p_test->p_test_regex == 0)
597 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
598 p_test->pz_test_text);
599 #endif
600 if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
601 return APPLY_FIX;
602 return SKIP_FIX;
606 /* * * * * * * * * * * * *
608 quoted_file_exists Make sure that a file exists before we emit
609 the file name. If we emit the name, our invoking shell will try
610 to copy a non-existing file into the destination directory. */
612 static int
613 quoted_file_exists (const char* pz_src_path,
614 const char* pz_file_path,
615 const char* pz_file)
617 char z[ MAXPATHLEN ];
618 char* pz;
619 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
620 pz = z + strlen ( z );
622 for (;;) {
623 char ch = *pz_file++;
624 if (! ISGRAPH( ch ))
625 return 0;
626 if (ch == '"')
627 break;
628 *pz++ = ch;
630 *pz = '\0';
632 struct stat s;
633 if (stat (z, &s) != 0)
634 return 0;
635 return S_ISREG( s.st_mode );
640 /* * * * * * * * * * * * *
642 extract_quoted_files
644 The syntax, `#include "file.h"' specifies that the compiler is to
645 search the local directory of the current file before the include
646 list. Consequently, if we have modified a header and stored it in
647 another directory, any files that are included by that modified
648 file in that fashion must also be copied into this new directory.
649 This routine finds those flavors of #include and for each one found
650 emits a triple of:
652 1. source directory of the original file
653 2. the relative path file name of the #includ-ed file
654 3. the full destination path for this file
656 Input: the text of the file, the file name and a pointer to the
657 match list where the match information was stored.
658 Result: internally nothing. The results are written to stdout
659 for interpretation by the invoking shell */
662 static void
663 extract_quoted_files (char* pz_data,
664 const char* pz_fixed_file,
665 regmatch_t* p_re_match)
667 char *pz_dir_end = strrchr (pz_fixed_file, '/');
668 char *pz_incl_quot = pz_data;
670 if (VLEVEL( VERB_APPLIES ))
671 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
673 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
674 If there is none, then it is in our current directory, ".". */
676 if (pz_dir_end == (char *) NULL)
677 pz_fixed_file = ".";
678 else
679 *pz_dir_end = '\0';
681 for (;;)
683 pz_incl_quot += p_re_match->rm_so;
685 /* Skip forward to the included file name */
686 while (*pz_incl_quot != '"')
687 pz_incl_quot++;
689 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
691 /* Print the source directory and the subdirectory
692 of the file in question. */
693 printf ("%s %s/", pz_src_dir, pz_fixed_file);
694 pz_dir_end = pz_incl_quot;
696 /* Append to the directory the relative path of the desired file */
697 while (*pz_incl_quot != '"')
698 putc (*pz_incl_quot++, stdout);
700 /* Now print the destination directory appended with the
701 relative path of the desired file */
702 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
703 while (*pz_dir_end != '"')
704 putc (*pz_dir_end++, stdout);
706 /* End of entry */
707 putc ('\n', stdout);
710 /* Find the next entry */
711 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
712 break;
717 /* * * * * * * * * * * * *
719 Somebody wrote a *_fix subroutine that we must call.
721 #ifndef SEPARATE_FIX_PROC
722 static int
723 internal_fix (int read_fd, tFixDesc* p_fixd)
725 int fd[2];
727 if (pipe( fd ) != 0)
729 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
730 exit (EXIT_FAILURE);
733 for (;;)
735 pid_t childid = fork();
737 switch (childid)
739 case -1:
740 break;
742 case 0:
743 close (fd[0]);
744 goto do_child_task;
746 default:
748 * Parent process
750 close (read_fd);
751 close (fd[1]);
752 return fd[0];
756 * Parent in error
758 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
759 p_fixd->fix_name);
761 static int failCt = 0;
762 if ((errno != EAGAIN) || (++failCt > 10))
763 exit (EXIT_FAILURE);
764 sleep (1);
766 } do_child_task:;
769 * Close our current stdin and stdout
771 close (STDIN_FILENO);
772 close (STDOUT_FILENO);
773 UNLOAD_DATA();
776 * Make the fd passed in the stdin, and the write end of
777 * the new pipe become the stdout.
779 dup2 (fd[1], STDOUT_FILENO);
780 dup2 (read_fd, STDIN_FILENO);
782 apply_fix (p_fixd, pz_curr_file);
783 exit (0);
785 #endif /* !SEPARATE_FIX_PROC */
788 #ifdef SEPARATE_FIX_PROC
789 static void
790 fix_with_system (tFixDesc* p_fixd,
791 tCC* pz_fix_file,
792 tCC* pz_file_source,
793 tCC* pz_temp_file)
795 char* pz_cmd;
796 char* pz_scan;
797 size_t argsize;
799 if (p_fixd->fd_flags & FD_SUBROUTINE)
801 static const char z_applyfix_prog[] =
802 "/../fixincludes/applyfix" EXE_EXT;
804 struct stat buf;
805 argsize = 32
806 + strlen (pz_orig_dir)
807 + sizeof (z_applyfix_prog)
808 + strlen (pz_fix_file)
809 + strlen (pz_file_source)
810 + strlen (pz_temp_file);
812 /* Allocate something sure to be big enough for our purposes */
813 pz_cmd = XNEWVEC (char, argsize);
814 strcpy (pz_cmd, pz_orig_dir);
815 pz_scan = pz_cmd + strlen (pz_orig_dir);
817 strcpy (pz_scan, z_applyfix_prog);
819 /* IF we can't find the "applyfix" executable file at the first guess,
820 try one level higher up */
821 if (stat (pz_cmd, &buf) == -1)
823 strcpy (pz_scan, "/..");
824 strcpy (pz_scan+3, z_applyfix_prog);
827 pz_scan += strlen (pz_scan);
830 * Now add the fix number and file names that may be needed
832 sprintf (pz_scan, " %ld '%s' '%s'", (long) (p_fixd - fixDescList),
833 pz_fix_file, pz_file_source, pz_temp_file);
835 else /* NOT an "internal" fix: */
837 size_t parg_size;
838 #ifdef __MSDOS__
839 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
840 dst is a temporary file anyway, so we know there's no other
841 file by that name; and DOS's system(3) doesn't mind to
842 clobber existing file in redirection. Besides, with DOS 8+3
843 limited file namespace, we can easily lose if dst already has
844 an extension that is 3 or more characters long.
846 I do not think the 8+3 issue is relevant because all the files
847 we operate on are named "*.h", making 8+2 adequate. Anyway,
848 the following bizarre use of 'cat' only works on DOS boxes.
849 It causes the file to be dropped into a temporary file for
850 'cat' to read (pipes do not work on DOS). */
851 tSCC z_cmd_fmt[] = " '%s' | cat > '%s'";
852 #else
853 /* Don't use positional formatting arguments because some lame-o
854 implementations cannot cope :-(. */
855 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
856 #endif
857 tCC** ppArgs = p_fixd->patch_args;
859 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
860 + strlen( pz_file_source );
861 parg_size = argsize;
865 * Compute the size of the command line. Add lotsa extra space
866 * because some of the args to sed use lotsa single quotes.
867 * (This requires three extra bytes per quote. Here we allow
868 * for up to 8 single quotes for each argument, including the
869 * command name "sed" itself. Nobody will *ever* need more. :)
871 for (;;)
873 tCC* p_arg = *(ppArgs++);
874 if (p_arg == NULL)
875 break;
876 argsize += 24 + strlen( p_arg );
879 /* Estimated buffer size we will need. */
880 pz_scan = pz_cmd = XNEWVEC (char, argsize);
881 /* How much of it do we allot to the program name and its
882 arguments. */
883 parg_size = argsize - parg_size;
885 ppArgs = p_fixd->patch_args;
888 * Copy the program name, unquoted
891 tCC* pArg = *(ppArgs++);
892 for (;;)
894 char ch = *(pArg++);
895 if (ch == NUL)
896 break;
897 *(pz_scan++) = ch;
902 * Copy the program arguments, quoted
904 for (;;)
906 tCC* pArg = *(ppArgs++);
907 char* pz_scan_save;
908 if (pArg == NULL)
909 break;
910 *(pz_scan++) = ' ';
911 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
912 parg_size - (pz_scan - pz_cmd) );
914 * Make sure we don't overflow the buffer due to sloppy
915 * size estimation.
917 while (pz_scan == (char*)NULL)
919 size_t already_filled = pz_scan_save - pz_cmd;
920 pz_cmd = xrealloc (pz_cmd, argsize += 100);
921 pz_scan_save = pz_scan = pz_cmd + already_filled;
922 parg_size += 100;
923 pz_scan = make_raw_shell_str( pz_scan, pArg,
924 parg_size - (pz_scan - pz_cmd) );
929 * add the file machinations.
931 #ifdef __MSDOS__
932 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
933 #else
934 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
935 pz_temp_file, pz_temp_file, pz_temp_file);
936 #endif
938 system( pz_cmd );
939 free( (void*)pz_cmd );
942 /* * * * * * * * * * * * *
944 This loop should only cycle for 1/2 of one loop.
945 "chain_open" starts a process that uses "read_fd" as
946 its stdin and returns the new fd this process will use
947 for stdout. */
949 #else /* is *NOT* SEPARATE_FIX_PROC */
950 static int
951 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
953 tCC* pz_cmd_save;
954 char* pz_cmd;
956 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
957 return internal_fix (read_fd, p_fixd);
959 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
961 pz_cmd = NULL;
962 pz_cmd_save = NULL;
964 else
966 tSCC z_cmd_fmt[] = "file='%s'\n%s";
967 pz_cmd = XNEWVEC (char, strlen (p_fixd->patch_args[2])
968 + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
969 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
970 pz_cmd_save = p_fixd->patch_args[2];
971 p_fixd->patch_args[2] = pz_cmd;
974 /* Start a fix process, handing off the previous read fd for its
975 stdin and getting a new fd that reads from the fix process' stdout.
976 We normally will not loop, but we will up to 10 times if we keep
977 getting "EAGAIN" errors.
980 for (;;)
982 static int failCt = 0;
983 int fd;
985 fd = chain_open (read_fd,
986 (tCC **) p_fixd->patch_args,
987 (process_chain_head == -1)
988 ? &process_chain_head : (pid_t *) NULL);
990 if (fd != -1)
992 read_fd = fd;
993 break;
996 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
997 p_fixd->fix_name);
999 if ((errno != EAGAIN) || (++failCt > 10))
1000 exit (EXIT_FAILURE);
1001 sleep (1);
1004 /* IF we allocated a shell script command,
1005 THEN free it and restore the command format to the fix description */
1006 if (pz_cmd != (char*)NULL)
1008 free ((void*)pz_cmd);
1009 p_fixd->patch_args[2] = pz_cmd_save;
1012 return read_fd;
1014 #endif
1017 /* * * * * * * * * * * * *
1019 * Process the potential fixes for a particular include file.
1020 * Input: the original text of the file and the file's name
1021 * Result: none. A new file may or may not be created.
1023 static t_bool
1024 fix_applies (tFixDesc* p_fixd)
1026 const char *pz_fname = pz_curr_file;
1027 const char *pz_scan = p_fixd->file_list;
1028 int test_ct;
1029 tTestDesc *p_test;
1031 #ifdef SEPARATE_FIX_PROC
1033 * There is only one fix that uses a shell script as of this writing.
1034 * I hope to nuke it anyway, it does not apply to DOS and it would
1035 * be painful to implement. Therefore, no "shell" fixes for DOS.
1037 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1038 return BOOL_FALSE;
1039 #else
1040 if (p_fixd->fd_flags & FD_SKIP_TEST)
1041 return BOOL_FALSE;
1042 #endif
1044 /* IF there is a file name restriction,
1045 THEN ensure the current file name matches one in the pattern */
1047 if (pz_scan != (char *) NULL)
1049 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1050 pz_fname += 2;
1052 for (;;)
1054 if (fnmatch (pz_scan, pz_fname, 0) == 0)
1055 break;
1056 pz_scan += strlen (pz_scan) + 1;
1057 if (*pz_scan == NUL)
1058 return BOOL_FALSE;
1062 /* FOR each test, see if it fails.
1063 IF it does fail, then we go on to the next test */
1065 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1066 test_ct-- > 0;
1067 p_test++)
1069 switch (p_test->type)
1071 case TT_TEST:
1072 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1073 #ifdef DEBUG
1074 if (VLEVEL( VERB_EVERYTHING ))
1075 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1076 pz_fname, p_fixd->test_ct - test_ct);
1077 #endif
1078 return BOOL_FALSE;
1080 break;
1082 case TT_EGREP:
1083 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1084 #ifdef DEBUG
1085 if (VLEVEL( VERB_EVERYTHING ))
1086 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1087 pz_fname, p_fixd->test_ct - test_ct);
1088 #endif
1089 return BOOL_FALSE;
1091 break;
1093 case TT_NEGREP:
1094 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1095 #ifdef DEBUG
1096 if (VLEVEL( VERB_EVERYTHING ))
1097 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1098 pz_fname, p_fixd->test_ct - test_ct);
1099 #endif
1100 /* Negated sense */
1101 return BOOL_FALSE;
1103 break;
1105 case TT_FUNCTION:
1106 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1107 != APPLY_FIX) {
1108 #ifdef DEBUG
1109 if (VLEVEL( VERB_EVERYTHING ))
1110 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1111 pz_fname, p_fixd->test_ct - test_ct);
1112 #endif
1113 return BOOL_FALSE;
1115 break;
1119 return BOOL_TRUE;
1123 /* * * * * * * * * * * * *
1125 Write out a replacement file */
1127 static void
1128 write_replacement (tFixDesc* p_fixd)
1130 const char* pz_text = p_fixd->patch_args[0];
1132 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1133 return;
1136 FILE* out_fp = create_file ();
1137 size_t sz = strlen (pz_text);
1138 fwrite (pz_text, sz, 1, out_fp);
1139 if (pz_text[ sz-1 ] != '\n')
1140 fputc ('\n', out_fp);
1141 fclose (out_fp);
1146 /* * * * * * * * * * * * *
1148 We have work to do. Read back in the output
1149 of the filtering chain. Compare each byte as we read it with
1150 the contents of the original file. As soon as we find any
1151 difference, we will create the output file, write out all
1152 the matched text and then copy any remaining data from the
1153 output of the filter chain.
1155 static void
1156 test_for_changes (int read_fd)
1158 FILE *in_fp = fdopen (read_fd, "r");
1159 FILE *out_fp = (FILE *) NULL;
1160 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
1162 #ifdef DO_STATS
1163 fixed_ct++;
1164 #endif
1165 for (;;)
1167 int ch;
1169 ch = getc (in_fp);
1170 if (ch == EOF)
1171 break;
1172 ch &= 0xFF; /* all bytes are 8 bits */
1174 /* IF we are emitting the output
1175 THEN emit this character, too.
1177 if (out_fp != (FILE *) NULL)
1178 putc (ch, out_fp);
1180 /* ELSE if this character does not match the original,
1181 THEN now is the time to start the output.
1183 else if (ch != *pz_cmp)
1185 out_fp = create_file ();
1187 #ifdef DO_STATS
1188 altered_ct++;
1189 #endif
1190 /* IF there are matched data, write the matched part now. */
1191 if ((char*)pz_cmp != pz_curr_data)
1192 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
1193 1, out_fp);
1195 /* Emit the current unmatching character */
1196 putc (ch, out_fp);
1198 else
1199 /* ELSE the character matches. Advance the compare ptr */
1200 pz_cmp++;
1203 /* IF we created the output file, ... */
1204 if (out_fp != (FILE *) NULL)
1206 regmatch_t match;
1208 /* Close the file and see if we have to worry about
1209 `#include "file.h"' constructs. */
1210 fclose (out_fp);
1211 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1212 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1215 fclose (in_fp);
1216 close (read_fd); /* probably redundant, but I'm paranoid */
1220 /* * * * * * * * * * * * *
1222 Process the potential fixes for a particular include file.
1223 Input: the original text of the file and the file's name
1224 Result: none. A new file may or may not be created. */
1226 void
1227 process (void)
1229 tFixDesc *p_fixd = fixDescList;
1230 int todo_ct = FIX_COUNT;
1231 int read_fd = -1;
1232 # ifndef SEPARATE_FIX_PROC
1233 int num_children = 0;
1234 # else /* is SEPARATE_FIX_PROC */
1235 char* pz_file_source = pz_curr_file;
1236 # endif
1238 if (access (pz_curr_file, R_OK) != 0)
1240 int erno = errno;
1241 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1242 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1243 erno, xstrerror (erno));
1244 return;
1247 pz_curr_data = load_file (pz_curr_file);
1248 if (pz_curr_data == (char *) NULL)
1249 return;
1251 #ifdef DO_STATS
1252 process_ct++;
1253 #endif
1254 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1255 fprintf (stderr, "%6lu %-50s \r",
1256 (unsigned long) data_map_size, pz_curr_file);
1258 # ifndef SEPARATE_FIX_PROC
1259 process_chain_head = NOPROCESS;
1261 /* For every fix in our fix list, ... */
1262 for (; todo_ct > 0; p_fixd++, todo_ct--)
1264 if (! fix_applies (p_fixd))
1265 continue;
1267 if (VLEVEL( VERB_APPLIES ))
1268 fprintf (stderr, "Applying %-24s to %s\n",
1269 p_fixd->fix_name, pz_curr_file);
1271 if (p_fixd->fd_flags & FD_REPLACEMENT)
1273 write_replacement (p_fixd);
1274 UNLOAD_DATA();
1275 return;
1278 /* IF we do not have a read pointer,
1279 THEN this is the first fix for the current file.
1280 Open the source file. That will be used as stdin for
1281 the first fix. Any subsequent fixes will use the
1282 stdout descriptor of the previous fix for its stdin. */
1284 if (read_fd == -1)
1286 read_fd = open (pz_curr_file, O_RDONLY);
1287 if (read_fd < 0)
1289 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1290 xstrerror (errno), pz_curr_file);
1291 exit (EXIT_FAILURE);
1294 /* Ensure we do not get duplicate output */
1296 fflush (stdout);
1299 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1300 num_children++;
1303 /* IF we have a read-back file descriptor,
1304 THEN check for changes and write output if changed. */
1306 if (read_fd >= 0)
1308 test_for_changes (read_fd);
1309 #ifdef DO_STATS
1310 apply_ct += num_children;
1311 #endif
1312 /* Wait for child processes created by chain_open()
1313 to avoid leaving zombies. */
1314 do {
1315 wait ((int *) NULL);
1316 } while (--num_children > 0);
1319 # else /* is SEPARATE_FIX_PROC */
1321 for (; todo_ct > 0; p_fixd++, todo_ct--)
1323 if (! fix_applies (p_fixd))
1324 continue;
1326 if (VLEVEL( VERB_APPLIES ))
1327 fprintf (stderr, "Applying %-24s to %s\n",
1328 p_fixd->fix_name, pz_curr_file);
1330 if (p_fixd->fd_flags & FD_REPLACEMENT)
1332 write_replacement (p_fixd);
1333 UNLOAD_DATA();
1334 return;
1336 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1337 pz_file_source = pz_temp_file;
1340 read_fd = open (pz_temp_file, O_RDONLY);
1341 if (read_fd < 0)
1343 if (errno != ENOENT)
1344 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1345 errno, xstrerror (errno), pz_temp_file);
1347 else
1349 test_for_changes (read_fd);
1350 /* Unlinking a file while it is still open is a Bad Idea on
1351 DOS/Windows. */
1352 close (read_fd);
1353 unlink (pz_temp_file);
1356 # endif
1357 UNLOAD_DATA();