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.
6 Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
8 This file is part of GNU CC.
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)
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.
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. */
27 #if defined( HAVE_MMAP_FILE )
29 #define BAD_ADDR ((void*)-1)
37 /* The contents of this string are not very important. It is mostly
38 just used as part of the "I am alive and working" test. */
40 static const char program_id[] = "fixincl version 1.1";
42 /* This format will be used at the start of every generated file */
44 static const char z_std_preamble[] =
45 "/* DO NOT EDIT THIS FILE.\n\n\
46 It has been auto-edited by fixincludes from:\n\n\
48 This had to be done to correct non-standard usages in the\n\
49 original, manufacturer supplied header file. */\n\n";
51 /* Working environment strings. Essentially, invocation 'options'. */
53 #define _ENV_(v,m,n,t) tCC* v = NULL;
57 int find_base_len = 0;
68 te_verbose verbose_level = VERB_PROGRESS;
71 #define VLEVEL(l) (verbose_level >= l)
72 #define NOT_SILENT VLEVEL(VERB_FIXES)
74 pid_t process_chain_head = (pid_t) -1;
76 char* pz_curr_file; /* name of the current file under test/fix */
77 char* pz_curr_data; /* original contents of that file */
78 char* pz_temp_file; /* for DOS, a place to stash the temporary
79 fixed data between system(3) calls */
80 t_bool curr_data_mapped;
83 size_t ttl_data_size = 0;
92 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
93 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
94 regex_t incl_quote_re;
97 char *load_file _P_((const char *));
98 void process _P_((char *, const char *));
100 void initialize _P_((int argc,char** argv));
103 /* External Source Code */
107 /* * * * * * * * * * * * * * * * * * *
118 initialize ( argc, argv );
120 have_tty = isatty (fileno (stderr));
122 /* Before anything else, ensure we can allocate our file name buffer. */
123 file_name_buf = load_file_data (stdin);
125 /* Because of the way server shells work, you have to keep stdin, out
126 and err open so that the proper input file does not get closed
129 freopen ("/dev/null", "r", stdin);
131 if (file_name_buf == (char *) NULL)
133 fputs ("No file names listed for fixing\n", stderr);
141 /* skip to start of name, past any "./" prefixes */
143 while (ISSPACE (*file_name_buf)) file_name_buf++;
144 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
147 /* Check for end of list */
149 if (*file_name_buf == NUL)
152 /* Set global file name pointer and find end of name */
154 pz_curr_file = file_name_buf;
155 pz_end = strchr( pz_curr_file, '\n' );
156 if (pz_end == (char*)NULL)
157 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
159 file_name_buf = pz_end + 1;
161 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
163 /* IF no name is found (blank line) or comment marker, skip line */
165 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
173 if (VLEVEL( VERB_PROGRESS )) {
176 Processed %5d files containing %d bytes \n\
177 Applying %5d fixes to %d files\n\
178 Altering %5d of them\n";
180 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
181 fixed_ct, altered_ct);
183 #endif /* DO_STATS */
186 unlink( pz_temp_file );
195 static const char zFmt[] = "echo '%s'";
198 /* The 'version' option is really used to test that:
199 1. The program loads correctly (no missing libraries)
200 2. that we can compile all the regular expressions.
201 3. we can correctly run our server shell process
204 sprintf (zBuf, zFmt, program_id);
207 exit (strcmp (run_shell (zBuf), program_id));
209 exit (system (zBuf));
213 /* * * * * * * * * * * * */
216 initialize ( argc, argv )
220 static const char var_not_found[] =
221 "fixincl ERROR: %s environment variable not defined\n"
223 "each of these must be defined:\n"
224 #define _ENV_(v,m,n,t) "\t" n " - " t "\n"
230 xmalloc_set_program_name (argv[0]);
238 if (strcmp (argv[1], "-v") == 0)
240 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
242 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
243 errno, xstrerror (errno), argv[1] );
249 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
253 #define _ENV_(v,m,n,t) { tSCC var[] = n; \
254 v = getenv (var); if (m && (v == NULL)) { \
255 fprintf (stderr, var_not_found, var); \
256 exit (EXIT_FAILURE); } }
262 if (isdigit( *pz_verbose ))
263 verbose_level = (te_verbose)atoi( pz_verbose );
265 switch (*pz_verbose) {
268 verbose_level = VERB_SILENT; break;
272 verbose_level = VERB_FIXES; break;
276 verbose_level = VERB_APPLIES; break;
280 verbose_level = VERB_PROGRESS; break;
284 verbose_level = VERB_TESTS; break;
288 verbose_level = VERB_EVERYTHING; break;
291 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
293 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
294 find_base_len = strlen( pz_find_base );
296 /* Compile all the regular expressions now.
297 That way, it is done only once for the whole run.
302 /* NULL as the first argument to `tempnam' causes it to DTRT
303 wrt the temporary directory where the file will be created. */
304 pz_temp_file = tempnam( NULL, "fxinc" );
307 signal (SIGQUIT, SIG_IGN);
309 signal (SIGIOT, SIG_IGN);
312 signal (SIGPIPE, SIG_IGN);
314 signal (SIGALRM, SIG_IGN);
315 signal (SIGTERM, SIG_IGN);
318 /* * * * * * * * * * * * *
320 load_file loads all the contents of a file into malloc-ed memory.
321 Its argument is the name of the file to read in; the returned
322 result is the NUL terminated contents of the file. The file
323 is presumed to be an ASCII text file containing no NULs. */
331 if (stat (fname, &stbf) != 0)
334 fprintf (stderr, "error %d (%s) stat-ing %s\n",
335 errno, xstrerror (errno), fname );
336 return (char *) NULL;
338 if (stbf.st_size == 0)
341 /* Make the data map size one larger than the file size for documentation
342 purposes. Truth is that there will be a following NUL character if
343 the file size is not a multiple of the page size. If it is a multiple,
344 then this adjustment sometimes fails anyway. */
345 data_map_size = stbf.st_size+1;
346 data_map_fd = open (fname, O_RDONLY);
347 ttl_data_size += data_map_size-1;
352 fprintf (stderr, "error %d (%s) opening %s for read\n",
353 errno, xstrerror (errno), fname);
357 #ifdef HAVE_MMAP_FILE
358 curr_data_mapped = BOOL_TRUE;
360 /* IF the file size is a multiple of the page size,
361 THEN sometimes you will seg fault trying to access a trailing byte */
362 if ((stbf.st_size & (getpagesize()-1)) == 0)
363 res = (char*)BAD_ADDR;
365 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
366 MAP_PRIVATE, data_map_fd, 0);
367 if (res == (char*)BAD_ADDR)
369 curr_data_mapped = BOOL_FALSE;
370 res = load_file_data ( fdopen (data_map_fd, "r"));
373 curr_data_mapped = BOOL_FALSE;
374 res = load_file_data ( fdopen (data_map_fd, "r"));
381 machine_matches( p_fixd )
385 tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
387 " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
388 tSCC skip[] = "skip"; /* 4 bytes */
389 tSCC run[] = "run"; /* 3 bytes */
390 /* total bytes to add to machine sum: 49 - see fixincl.tpl */
392 const char **papz_machs = p_fixd->papz_machs;
397 char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
399 /* Start the case statement */
401 sprintf (cmd_buf, case_fmt, pz_machine);
402 pz = cmd_buf + strlen (cmd_buf);
404 /* Determine if a match means to apply the fix or not apply it */
406 if (p_fixd->fd_flags & FD_MACH_IFNOT)
417 /* Emit all the machine names. If there are more than one,
418 then we will insert " | \\\n" between the names */
422 const char* pz_mach = *(papz_machs++);
424 if (pz_mach == (const char*) NULL)
426 sprintf (pz, "%s%s", pz_sep, pz_mach);
431 /* Now emit the match and not-match actions and the esac */
433 sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
436 The result will start either with 's' or 'r'. */
440 pz = run_shell (cmd_buf);
445 p_fixd->fd_flags |= FD_SKIP_TEST;
451 # else /* is __MSDOS__ */
452 const char **papz_machs = p_fixd->papz_machs;
453 int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
456 const char* pz_mach = *(papz_machs++);
458 if (pz_mach == (const char*) NULL)
460 if (strstr (pz_mach, "dos") != NULL && !invert)
464 p_fixd->fd_flags |= FD_SKIP_TEST;
469 /* * * * * * * * * * * * *
471 run_compiles run all the regexp compiles for all the fixes once.
476 tFixDesc *p_fixd = fixDescList;
477 int fix_ct = FIX_COUNT;
478 regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
480 if (p_re == (regex_t *) NULL)
482 fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n",
483 REGEX_COUNT * sizeof (regex_t));
487 /* Make sure compile_re does not stumble across invalid data */
489 memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
490 memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
492 compile_re (incl_quote_pat, &incl_quote_re, 1,
493 "quoted include", "run_compiles");
495 /* Allow machine name tests to be ignored (testing, mainly) */
497 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
498 pz_machine = (char*)NULL;
500 /* FOR every fixup, ... */
503 tTestDesc *p_test = p_fixd->p_test_desc;
504 int test_ct = p_fixd->test_ct;
506 /* IF the machine type pointer is not NULL (we are not in test mode)
507 AND this test is for or not done on particular machines
510 if ( (pz_machine != NULL)
511 && (p_fixd->papz_machs != (const char**) NULL)
512 && ! machine_matches (p_fixd) )
515 /* FOR every test for the fixup, ... */
517 while (--test_ct >= 0)
519 switch (p_test->type)
523 p_test->p_test_regex = p_re++;
524 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
525 "select test", p_fixd->fix_name);
530 while (p_fixd++, --fix_ct > 0);
534 /* * * * * * * * * * * * *
536 create_file Create the output modified file.
537 Input: the name of the file to create
538 Returns: a file pointer to the new, open file */
540 #if defined(S_IRUSR) && defined(S_IWUSR) && \
541 defined(S_IRGRP) && defined(S_IROTH)
543 # define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
545 # define S_IRALL 0644
548 #if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
549 defined(S_IROTH) && defined(S_IXOTH)
551 # define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
553 # define S_DIRALL 0755
562 char fname[MAXPATHLEN];
564 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
566 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
568 /* We may need to create the directories needed... */
569 if ((fd < 0) && (errno == ENOENT))
571 char *pz_dir = strchr (fname + 1, '/');
574 while (pz_dir != (char *) NULL)
577 if (stat (fname, &stbf) < 0)
579 mkdir (fname, S_IFDIR | S_DIRALL);
583 pz_dir = strchr (pz_dir + 1, '/');
586 /* Now, lets try the open again... */
587 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
591 fprintf (stderr, "Error %d (%s) creating %s\n",
592 errno, xstrerror (errno), fname);
596 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
597 pf = fdopen (fd, "w");
600 * IF pz_machine is NULL, then we are in some sort of test mode.
601 * Do not insert the current directory name. Use a constant string.
603 fprintf (pf, z_std_preamble,
613 /* * * * * * * * * * * * *
615 test_test make sure a shell-style test expression passes.
616 Input: a pointer to the descriptor of the test to run and
617 the name of the file that we might want to fix
618 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
619 shell script we run. */
622 test_test (p_test, pz_test_file)
628 if ( test %s ) > /dev/null 2>&1\n\
636 static char cmd_buf[4096];
638 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
639 pz_res = run_shell (cmd_buf);
651 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
655 free ((void *) pz_res);
660 * IF we are in MS-DOS land, then whatever shell-type test is required
661 * will, by definition, fail
663 #define test_test(t,tf) SKIP_FIX
666 /* * * * * * * * * * * * *
668 egrep_test make sure an egrep expression is found in the file text.
669 Input: a pointer to the descriptor of the test to run and
670 the pointer to the contents of the file under suspicion
671 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
673 The caller may choose to reverse meaning if the sense of the test
677 egrep_test (pz_data, p_test)
682 if (p_test->p_test_regex == 0)
683 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
684 p_test->pz_test_text);
686 if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
692 /* * * * * * * * * * * * *
694 quoted_file_exists Make sure that a file exists before we emit
695 the file name. If we emit the name, our invoking shell will try
696 to copy a non-existing file into the destination directory. */
699 quoted_file_exists (pz_src_path, pz_file_path, pz_file)
704 char z[ MAXPATHLEN ];
706 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
707 pz = z + strlen ( z );
710 char ch = *pz_file++;
720 if (stat (z, &s) != 0)
722 return S_ISREG( s.st_mode );
727 /* * * * * * * * * * * * *
731 The syntax, `#include "file.h"' specifies that the compiler is to
732 search the local directory of the current file before the include
733 list. Consequently, if we have modified a header and stored it in
734 another directory, any files that are included by that modified
735 file in that fashion must also be copied into this new directory.
736 This routine finds those flavors of #include and for each one found
739 1. source directory of the original file
740 2. the relative path file name of the #includ-ed file
741 3. the full destination path for this file
743 Input: the text of the file, the file name and a pointer to the
744 match list where the match information was stored.
745 Result: internally nothing. The results are written to stdout
746 for interpretation by the invoking shell */
750 extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
752 const char *pz_fixed_file;
753 regmatch_t *p_re_match;
755 char *pz_dir_end = strrchr (pz_fixed_file, '/');
756 char *pz_incl_quot = pz_data;
758 if (VLEVEL( VERB_APPLIES ))
759 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
761 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
762 If there is none, then it is in our current directory, ".". */
764 if (pz_dir_end == (char *) NULL)
771 pz_incl_quot += p_re_match->rm_so;
773 /* Skip forward to the included file name */
774 while (ISSPACE (*pz_incl_quot))
776 /* ISSPACE() may evaluate its argument more than once! */
777 while (++pz_incl_quot, ISSPACE (*pz_incl_quot))
779 pz_incl_quot += sizeof ("include") - 1;
780 while (*pz_incl_quot++ != '"')
783 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
785 /* Print the source directory and the subdirectory
786 of the file in question. */
787 printf ("%s %s/", pz_src_dir, pz_fixed_file);
788 pz_dir_end = pz_incl_quot;
790 /* Append to the directory the relative path of the desired file */
791 while (*pz_incl_quot != '"')
792 putc (*pz_incl_quot++, stdout);
794 /* Now print the destination directory appended with the
795 relative path of the desired file */
796 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
797 while (*pz_dir_end != '"')
798 putc (*pz_dir_end++, stdout);
804 /* Find the next entry */
805 if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
811 /* * * * * * * * * * * * *
813 Somebody wrote a *_fix subroutine that we must call.
817 internal_fix (read_fd, p_fixd)
825 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
831 pid_t childid = fork();
854 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
857 static int failCt = 0;
858 if ((errno != EAGAIN) || (++failCt > 10))
865 * Close our current stdin and stdout
867 close (STDIN_FILENO);
868 close (STDOUT_FILENO);
872 * Make the fd passed in the stdin, and the write end of
873 * the new pipe become the stdout.
875 fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
876 fcntl (read_fd, F_DUPFD, STDIN_FILENO);
878 apply_fix (p_fixd, pz_curr_file);
881 #endif /* !__MSDOS__ */
886 fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
896 if (p_fixd->fd_flags & FD_SUBROUTINE)
898 tSCC z_applyfix_prog[] = "/fixinc/applyfix";
901 + strlen( pz_orig_dir )
902 + sizeof( z_applyfix_prog )
903 + strlen( pz_fix_file )
904 + strlen( pz_file_source )
905 + strlen( pz_temp_file );
907 pz_cmd = (char*)xmalloc( argsize );
909 strcpy( pz_cmd, pz_orig_dir );
910 pz_scan = pz_cmd + strlen( pz_orig_dir );
911 strcpy( pz_scan, z_applyfix_prog );
912 pz_scan += sizeof( z_applyfix_prog ) - 1;
916 * Now add the fix number and file names that may be needed
918 sprintf (pz_scan, "%ld %s %s %s", p_fixd - fixDescList,
919 pz_fix_file, pz_file_source, pz_temp_file);
921 else /* NOT an "internal" fix: */
924 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
925 dst is a temporary file anyway, so we know there's no other
926 file by that name; and DOS's system(3) doesn't mind to
927 clobber existing file in redirection. Besides, with DOS 8+3
928 limited file namespace, we can easily lose if dst already has
929 an extension that is 3 or more characters long.
930 The following bizarre use of 'cat' only works on DOS boxes.
931 It is causing the file to be dropped into a temporary file for
932 'cat' to read (pipes do not work on DOS). */
933 tSCC z_cmd_fmt[] = " %s | cat > %s";
934 tCC** ppArgs = p_fixd->patch_args;
936 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
937 + strlen( pz_file_source );
942 * Compute the size of the command line. Add lotsa extra space
943 * because some of the args to sed use lotsa single quotes.
944 * (This requires three extra bytes per quote. Here we allow
945 * for up to 8 single quotes for each argument, including the
946 * command name "sed" itself. Nobody will *ever* need more. :)
950 tCC* p_arg = *(ppArgs++);
953 argsize += 24 + strlen( p_arg );
956 /* Estimated buffer size we will need. */
957 pz_scan = pz_cmd = (char*)xmalloc( argsize );
958 /* How much of it do we allot to the program name and its
960 parg_size = argsize - parg_size;
962 ppArgs = p_fixd->patch_args;
965 * Copy the program name, unquoted
968 tCC* pArg = *(ppArgs++);
979 * Copy the program arguments, quoted
983 tCC* pArg = *(ppArgs++);
988 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
989 parg_size - (pz_scan - pz_cmd) );
991 * Make sure we don't overflow the buffer due to sloppy
994 while (pz_scan == (char*)NULL)
996 size_t already_filled = pz_scan_save - pz_cmd;
997 pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
998 pz_scan_save = pz_scan = pz_cmd + already_filled;
1000 pz_scan = make_raw_shell_str( pz_scan, pArg,
1001 parg_size - (pz_scan - pz_cmd) );
1006 * add the file machinations.
1008 sprintf( pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1011 free( (void*)pz_cmd );
1014 /* * * * * * * * * * * * *
1016 This loop should only cycle for 1/2 of one loop.
1017 "chain_open" starts a process that uses "read_fd" as
1018 its stdin and returns the new fd this process will use
1021 #else /* is *NOT* __MSDOS__ */
1023 start_fixer (read_fd, p_fixd, pz_fix_file)
1031 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1032 return internal_fix (read_fd, p_fixd);
1034 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
1035 pz_cmd = (char*)NULL;
1038 tSCC z_cmd_fmt[] = "file='%s'\n%s";
1039 pz_cmd = (char*)malloc (strlen (p_fixd->patch_args[2])
1040 + sizeof( z_cmd_fmt )
1041 + strlen( pz_fix_file ));
1042 if (pz_cmd == (char*)NULL)
1044 fputs ("allocation failure\n", stderr);
1045 exit (EXIT_FAILURE);
1047 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
1048 pz_cmd_save = p_fixd->patch_args[2];
1049 p_fixd->patch_args[2] = pz_cmd;
1052 /* Start a fix process, handing off the previous read fd for its
1053 stdin and getting a new fd that reads from the fix process' stdout.
1054 We normally will not loop, but we will up to 10 times if we keep
1055 getting "EAGAIN" errors.
1060 static int failCt = 0;
1063 fd = chain_open (read_fd,
1064 (t_pchar *) p_fixd->patch_args,
1065 (process_chain_head == -1)
1066 ? &process_chain_head : (pid_t *) NULL);
1074 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
1077 if ((errno != EAGAIN) || (++failCt > 10))
1078 exit (EXIT_FAILURE);
1082 /* IF we allocated a shell script command,
1083 THEN free it and restore the command format to the fix description */
1084 if (pz_cmd != (char*)NULL)
1086 free ((void*)pz_cmd);
1087 p_fixd->patch_args[2] = pz_cmd_save;
1095 /* * * * * * * * * * * * *
1097 Process the potential fixes for a particular include file.
1098 Input: the original text of the file and the file's name
1099 Result: none. A new file may or may not be created. */
1102 fix_applies (p_fixd)
1105 const char *pz_fname = pz_curr_file;
1106 const char *pz_scan = p_fixd->file_list;
1112 * There is only one fix that uses a shell script as of this writing.
1113 * I hope to nuke it anyway, it does not apply to DOS and it would
1114 * be painful to implement. Therefore, no "shell" fixes for DOS.
1116 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1119 if (p_fixd->fd_flags & FD_SKIP_TEST)
1123 /* IF there is a file name restriction,
1124 THEN ensure the current file name matches one in the pattern */
1126 if (pz_scan != (char *) NULL)
1130 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1132 name_len = strlen (pz_fname);
1136 pz_scan = strstr (pz_scan + 1, pz_fname);
1137 /* IF we can't match the string at all,
1139 if (pz_scan == (char *) NULL)
1142 /* IF the match is surrounded by the '|' markers,
1143 THEN we found a full match -- time to run the tests */
1145 if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
1150 /* FOR each test, see if it fails.
1151 IF it does fail, then we go on to the next test */
1153 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1157 switch (p_test->type)
1160 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
1162 if (VLEVEL( VERB_EVERYTHING ))
1163 fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
1164 pz_fname, p_fixd->test_ct - test_ct);
1171 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
1173 if (VLEVEL( VERB_EVERYTHING ))
1174 fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
1175 pz_fname, p_fixd->test_ct - test_ct);
1182 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
1184 if (VLEVEL( VERB_EVERYTHING ))
1185 fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
1186 pz_fname, p_fixd->test_ct - test_ct);
1194 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
1197 if (VLEVEL( VERB_EVERYTHING ))
1198 fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
1199 pz_fname, p_fixd->test_ct - test_ct);
1211 /* * * * * * * * * * * * *
1213 Write out a replacement file */
1216 write_replacement (p_fixd)
1219 const char* pz_text = p_fixd->patch_args[0];
1221 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1225 FILE* out_fp = create_file (pz_curr_file);
1226 fputs (pz_text, out_fp);
1232 /* * * * * * * * * * * * *
1234 We have work to do. Read back in the output
1235 of the filtering chain. Compare each byte as we read it with
1236 the contents of the original file. As soon as we find any
1237 difference, we will create the output file, write out all
1238 the matched text and then copy any remaining data from the
1239 output of the filter chain.
1242 test_for_changes (read_fd)
1245 FILE *in_fp = fdopen (read_fd, "r");
1246 FILE *out_fp = (FILE *) NULL;
1247 char *pz_cmp = pz_curr_data;
1260 /* IF we are emitting the output
1261 THEN emit this character, too.
1263 if (out_fp != (FILE *) NULL)
1266 /* ELSE if this character does not match the original,
1267 THEN now is the time to start the output.
1269 else if (ch != *pz_cmp)
1271 out_fp = create_file (pz_curr_file);
1276 /* IF there are matched data, write the matched part now. */
1277 if (pz_cmp != pz_curr_data)
1278 fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
1280 /* Emit the current unmatching character */
1284 /* ELSE the character matches. Advance the compare ptr */
1288 /* IF we created the output file, ... */
1289 if (out_fp != (FILE *) NULL)
1293 /* Close the file and see if we have to worry about
1294 `#include "file.h"' constructs. */
1296 if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
1297 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1301 close (read_fd); /* probably redundant, but I'm paranoid */
1305 /* * * * * * * * * * * * *
1307 Process the potential fixes for a particular include file.
1308 Input: the original text of the file and the file's name
1309 Result: none. A new file may or may not be created. */
1314 tFixDesc *p_fixd = fixDescList;
1315 int todo_ct = FIX_COUNT;
1318 int num_children = 0;
1319 # else /* is __MSDOS__ */
1320 char* pz_file_source = pz_curr_file;
1323 if (access (pz_curr_file, R_OK) != 0)
1326 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
1327 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
1328 erno, xstrerror (erno));
1332 pz_curr_data = load_file (pz_curr_file);
1333 if (pz_curr_data == (char *) NULL)
1339 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1340 fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
1343 process_chain_head = NOPROCESS;
1345 /* For every fix in our fix list, ... */
1346 for (; todo_ct > 0; p_fixd++, todo_ct--)
1348 if (! fix_applies (p_fixd))
1351 if (VLEVEL( VERB_APPLIES ))
1352 fprintf (stderr, "Applying %-24s to %s\n",
1353 p_fixd->fix_name, pz_curr_file);
1355 if (p_fixd->fd_flags & FD_REPLACEMENT)
1357 write_replacement (p_fixd);
1362 /* IF we do not have a read pointer,
1363 THEN this is the first fix for the current file.
1364 Open the source file. That will be used as stdin for
1365 the first fix. Any subsequent fixes will use the
1366 stdout descriptor of the previous fix for its stdin. */
1370 read_fd = open (pz_curr_file, O_RDONLY);
1373 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
1374 xstrerror (errno), pz_curr_file);
1375 exit (EXIT_FAILURE);
1378 /* Ensure we do not get duplicate output */
1383 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
1387 /* IF we have a read-back file descriptor,
1388 THEN check for changes and write output if changed. */
1392 test_for_changes (read_fd);
1394 apply_ct += num_children;
1396 /* Wait for child processes created by chain_open()
1397 to avoid leaving zombies. */
1399 wait ((int *) NULL);
1400 } while (--num_children > 0);
1403 # else /* is __MSDOS__ */
1405 for (; todo_ct > 0; p_fixd++, todo_ct--)
1407 if (! fix_applies (p_fixd))
1410 if (VLEVEL( VERB_APPLIES ))
1411 fprintf (stderr, "Applying %-24s to %s\n",
1412 p_fixd->fix_name, pz_curr_file);
1414 if (p_fixd->fd_flags & FD_REPLACEMENT)
1416 write_replacement (p_fixd);
1420 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1421 pz_file_source = pz_temp_file;
1424 read_fd = open( pz_temp_file, O_RDONLY );
1425 test_for_changes( read_fd );
1426 /* Unlinking a file while it is still open is a Bad Idea on
1429 unlink( pz_temp_file );