files which are fixed to work correctly with ANSI C and placed in a
directory that GNU C will search.
- Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#include "auto-host.h"
+#include "fixlib.h"
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
+#if HAVE_MMAP_FILE
+#include <sys/mman.h>
+#define BAD_ADDR ((void*)-1)
#endif
+
#include <signal.h>
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#include <ctype.h>
-#include "gnu-regex.h"
#include "server.h"
-static const char program_id[] = "fixincl version 1.0";
-
-#define MINIMUM_MAXIMUM_LINES 128
-
-/* If this particular system's header files define the macro `MAXPATHLEN',
- we happily take advantage of it; otherwise we use a value which ought
- to be large enough. */
-#ifndef MAXPATHLEN
-# define MAXPATHLEN 4096
-#endif
-#define NAME_TABLE_SIZE (MINIMUM_MAXIMUM_LINES * MAXPATHLEN)
-
-char *file_name_buf;
+/* The contents of this string are not very important. It is mostly
+ just used as part of the "I am alive and working" test. */
-#define tSCC static const char
-#define tCC const char
-#define tSC static char
+static const char program_id[] = "fixincl version 1.1";
-typedef int t_success;
+/* This format will be used at the start of every generated file */
-#define FAILURE (-1)
-#define SUCCESS 0
-#define PROBLEM 1
-
-#define SUCCEEDED(p) ((p) == SUCCESS)
-#define SUCCESSFUL(p) SUCCEEDED (p)
-#define FAILED(p) ((p) < SUCCESS)
-#define HADGLITCH(p) ((p) > SUCCESS)
-
-#define NUL '\0'
-
-/* Test Descriptor
-
- Each fix may have associated tests that determine
- whether the fix needs to be applied or not.
- Each test has a type (from the te_test_type enumeration);
- associated test text; and, if the test is TT_EGREP or
- the negated form TT_NEGREP, a pointer to the compiled
- version of the text string.
-
- */
-typedef enum
-{
- TT_TEST, TT_EGREP, TT_NEGREP
-} te_test_type;
+static const char z_std_preamble[] =
+"/* DO NOT EDIT THIS FILE.\n\n\
+ It has been auto-edited by fixincludes from:\n\n\
+\t\"%s/%s\"\n\n\
+ This had to be done to correct non-standard usages in the\n\
+ original, manufacturer supplied header file. */\n\n";
-typedef struct test_desc tTestDesc;
+/* Working environment strings. Essentially, invocation 'options'. */
-struct test_desc
-{
- te_test_type type;
- const char *pz_test_text;
- regex_t *p_test_regex;
-};
+#define _ENV_(v,m,n,t) tCC* v = NULL;
+ENV_TABLE
+#undef _ENV_
-typedef struct patch_desc tPatchDesc;
+int find_base_len = 0;
-/* Fix Descriptor
+typedef enum {
+ VERB_SILENT = 0,
+ VERB_FIXES,
+ VERB_APPLIES,
+ VERB_PROGRESS,
+ VERB_TESTS,
+ VERB_EVERYTHING
+} te_verbose;
- Everything you ever wanted to know about how to apply
- a particular fix (which files, how to qualify them,
- how to actually make the fix, etc...)
+te_verbose verbose_level = VERB_PROGRESS;
+int have_tty = 0;
- */
-#define FD_MACH_ONLY 0x0000
-#define FD_MACH_IFNOT 0x0001
-#define FD_SKIP_TEST 0x8000
+#define VLEVEL(l) (verbose_level >= l)
+#define NOT_SILENT VLEVEL(VERB_FIXES)
-typedef struct fix_desc tFixDesc;
-struct fix_desc
-{
- const char* fix_name; /* Name of the fix */
- const char* file_list; /* List of files it applies to */
- const char** papz_machs; /* List of machine/os-es it applies to */
- regex_t* unused;
- int test_ct;
- int fd_flags;
- tTestDesc* p_test_desc;
- const char** patch_args;
-};
+pid_t process_chain_head = (pid_t) -1;
-/* Working environment strings. Essentially, invocation 'options'. */
-char *pz_dest_dir = NULL;
-char *pz_src_dir = NULL;
-char *pz_machine = NULL;
-char *pz_find_base = NULL;
-int find_base_len = 0;
+char* pz_curr_file; /* name of the current file under test/fix */
+char* pz_curr_data; /* original contents of that file */
+t_bool curr_data_mapped;
+int data_map_fd;
+size_t data_map_size;
+size_t ttl_data_size = 0;
-pid_t process_chain_head = (pid_t) -1;
+#ifdef DO_STATS
+int process_ct = 0;
+int apply_ct = 0;
+int fixed_ct = 0;
+int altered_ct = 0;
+#endif /* DO_STATS */
const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
+tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
regex_t incl_quote_re;
+void do_version ();
char *load_file _P_((const char *));
void process _P_((char *, const char *));
void run_compiles ();
-void wait_for_pid _P_(( pid_t ));
-void initialize ();
+void initialize _P_((int argc,char** argv));
+void process ();
+
+/* External Source Code */
#include "fixincl.x"
int argc;
char **argv;
{
- static const char gnu_lib_mark[] =
- "This file is part of the GNU C Library";
+ char *file_name_buf;
-#ifndef NO_BOGOSITY_LIMITS
-# define BOGUS_LIMIT MINIMUM_MAXIMUM_LINES
- size_t loop_ct;
-#endif
+ initialize ( argc, argv );
- char *apz_names[BOGUS_LIMIT];
- size_t file_name_ct;
+ have_tty = isatty (fileno (stderr));
/* Before anything else, ensure we can allocate our file name buffer. */
- file_name_buf = (char *) malloc (NAME_TABLE_SIZE);
- if (file_name_buf == (char *) NULL)
- {
- fprintf (stderr, "fixincl cannot allocate 0x%08X bytes\n",
- NAME_TABLE_SIZE);
- exit (EXIT_FAILURE);
- }
-
- switch (argc)
- {
- case 1:
- break;
+ file_name_buf = load_file_data (stdin);
- case 2:
- if (strcmp (argv[1], "-v") == 0)
- {
- static const char zFmt[] = "echo '%s'";
+ /* Because of the way server shells work, you have to keep stdin, out
+ and err open so that the proper input file does not get closed
+ by accident */
- /* The 'version' option is really used to test that:
- 1. The program loads correctly (no missing libraries)
- 2. we can correctly run our server shell process
- 3. that we can compile all the regular expressions.
- */
- run_compiles ();
- sprintf (file_name_buf, zFmt, program_id);
- fputs (file_name_buf + 5, stdout);
- exit (strcmp (run_shell (file_name_buf), program_id));
- }
- freopen (argv[1], "r", stdin);
- break;
+ freopen ("/dev/null", "r", stdin);
- default:
- fputs ("fixincl ERROR: too many command line arguments\n", stderr);
+ if (file_name_buf == (char *) NULL)
+ {
+ fputs ("No file names listed for fixing\n", stderr);
exit (EXIT_FAILURE);
}
- initialize ();
-
-#ifndef NO_BOGOSITY_LIMITS
- /* Some systems only allow so many calls to fork(2).
- This is inadequate for this program. Consequently,
- we must let a grandfather process spawn children
- that then spawn all the processes that do the real work.
- */
for (;;)
{
- file_name_ct = 0;
+ char* pz_end;
- {
- char *pz_buf = file_name_buf;
+ /* skip to start of name, past any "./" prefixes */
- /* Only the parent process can read from stdin without confusing
- the world. (How does the child tell the parent to skip
- forward? Pipes and files behave differently.) */
+ while (ISSPACE (*file_name_buf)) file_name_buf++;
+ while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
+ file_name_buf += 2;
- while ( (file_name_ct < BOGUS_LIMIT)
- && (pz_buf < (file_name_buf + NAME_TABLE_SIZE - MAXPATHLEN)))
- {
- if (fgets (pz_buf, MAXPATHLEN, stdin) == (char *) NULL)
- break;
- while (isspace (*pz_buf))
- pz_buf++;
- if ((*pz_buf == '\0') || (*pz_buf == '#'))
- continue;
- apz_names[file_name_ct++] = pz_buf;
- pz_buf += strlen (pz_buf);
- while (isspace (pz_buf[-1]))
- pz_buf--;
- *pz_buf++ = '\0';
- }
- }
-
- /* IF we did not get any files this time thru
- THEN we must be done. */
- if (file_name_ct == 0)
- return EXIT_SUCCESS;
+ /* Check for end of list */
- fflush (stdout);
- fflush (stderr);
-
- {
- pid_t child = fork ();
- if (child == NULLPROCESS)
- break;
+ if (*file_name_buf == NUL)
+ break;
- if (child == NOPROCESS)
- {
- fprintf (stderr, "Error %d (%s) forking in main\n",
- errno, strerror (errno));
- exit (EXIT_FAILURE);
- }
+ /* Set global file name pointer and find end of name */
-#ifdef DEBUG
- fprintf (stderr, "Waiting for %d to complete %d files\n",
- child, file_name_ct);
-#endif
+ pz_curr_file = file_name_buf;
+ pz_end = strchr( pz_curr_file, '\n' );
+ if (pz_end == (char*)NULL)
+ pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
+ else
+ file_name_buf = pz_end + 1;
- wait_for_pid( child, file_name_ct );
- }
- }
-#else
-#error "NON-BOGUS LIMITS NOT SUPPORTED?!?!"
-#endif
+ while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
- /*
- Here we are the child of the grandparent process. The parent
- of all the little fixup processes. We ignore the deaths of
- our children. */
+ /* IF no name is found (blank line) or comment marker, skip line */
- signal (SIGCLD, SIG_IGN);
+ if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
+ continue;
+ *pz_end = NUL;
- /* For every file specified in stdandard in
- (except as throttled for bogus reasons)...
- */
- for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++)
- {
- char *pz_data;
- char *pz_file_name = apz_names[loop_ct];
+ process ();
+ } /* for (;;) */
- if (access (pz_file_name, R_OK) != 0)
- {
- int erno = errno;
- fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
- pz_file_name, getcwd ((char *) NULL, MAXPATHLEN),
- erno, strerror (erno));
- }
- else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL))
- {
- if (strstr (pz_data, gnu_lib_mark) == (char *) NULL)
- process (pz_data, pz_file_name);
- free ((void *) pz_data);
- }
- }
+#ifdef DO_STATS
+ if (VLEVEL( VERB_PROGRESS )) {
+ tSCC zFmt[] =
+ "\
+Processed %5d files containing %d bytes \n\
+Applying %5d fixes to %d files\n\
+Altering %5d of them\n";
+ fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
+ fixed_ct, altered_ct);
+ }
+#endif /* DO_STATS */
return EXIT_SUCCESS;
}
+void
+do_version ()
+{
+ static const char zFmt[] = "echo '%s'";
+ char zBuf[ 1024 ];
+
+ /* The 'version' option is really used to test that:
+ 1. The program loads correctly (no missing libraries)
+ 2. that we can compile all the regular expressions.
+ 3. we can correctly run our server shell process
+ */
+ run_compiles ();
+ sprintf (zBuf, zFmt, program_id);
+ puts (zBuf + 5);
+ exit (strcmp (run_shell (zBuf), program_id));
+}
+
/* * * * * * * * * * * * */
void
-initialize()
+initialize ( argc, argv )
+ int argc;
+ char** argv;
{
static const char var_not_found[] =
- "fixincl ERROR: %s environment variable not defined\n";
+ "fixincl ERROR: %s environment variable not defined\n"
+#ifdef __STDC__
+ "each of these must be defined:\n"
+#define _ENV_(v,m,n,t) "\t" n " - " t "\n"
+ENV_TABLE
+#undef _ENV_
+#endif
+ ;
- {
- static const char var[] = "TARGET_MACHINE";
- pz_machine = getenv (var);
- if (pz_machine == (char *) NULL)
- {
- fprintf (stderr, var_not_found, var);
- exit (EXIT_FAILURE);
- }
- }
+ xmalloc_set_program_name (argv[0]);
- {
- static const char var[] = "DESTDIR";
- pz_dest_dir = getenv (var);
- if (pz_dest_dir == (char *) NULL)
- {
- fprintf (stderr, var_not_found, var);
- exit (EXIT_FAILURE);
- }
- }
+ switch (argc)
+ {
+ case 1:
+ break;
- {
- static const char var[] = "SRCDIR";
- pz_src_dir = getenv (var);
- if (pz_src_dir == (char *) NULL)
- {
- fprintf (stderr, var_not_found, var);
- exit (EXIT_FAILURE);
- }
- }
+ case 2:
+ if (strcmp (argv[1], "-v") == 0)
+ do_version ();
+ if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
+ {
+ fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
+ errno, xstrerror (errno), argv[1] );
+ exit (EXIT_FAILURE);
+ }
+ break;
- {
- static const char var[] = "FIND_BASE";
- pz_find_base = getenv (var);
- if (pz_find_base == (char *) NULL)
- {
- fprintf (stderr, var_not_found, var);
- exit (EXIT_FAILURE);
- }
- find_base_len = strlen( pz_find_base );
- }
+ default:
+ fputs ("fixincl ERROR: too many command line arguments\n", stderr);
+ exit (EXIT_FAILURE);
+ }
+
+#define _ENV_(v,m,n,t) { tSCC var[] = n; \
+ v = getenv (var); if (m && (v == NULL)) { \
+ fprintf (stderr, var_not_found, var); \
+ exit (EXIT_FAILURE); } }
+
+ENV_TABLE
+
+#undef _ENV_
+
+ if (isdigit( *pz_verbose ))
+ verbose_level = (te_verbose)atoi( pz_verbose );
+ else
+ switch (*pz_verbose) {
+ case 's':
+ case 'S':
+ verbose_level = VERB_SILENT; break;
+
+ case 'f':
+ case 'F':
+ verbose_level = VERB_FIXES; break;
+
+ case 'a':
+ case 'A':
+ verbose_level = VERB_APPLIES; break;
+
+ case 'p':
+ case 'P':
+ verbose_level = VERB_PROGRESS; break;
+
+ case 't':
+ case 'T':
+ verbose_level = VERB_TESTS; break;
+
+ case 'e':
+ case 'E':
+ verbose_level = VERB_EVERYTHING; break;
+ }
+
+ while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
+ pz_find_base += 2;
+ if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
+ find_base_len = strlen( pz_find_base );
/* Compile all the regular expressions now.
That way, it is done only once for the whole run.
run_compiles ();
signal (SIGQUIT, SIG_IGN);
+#ifdef SIGIOT
signal (SIGIOT, SIG_IGN);
+#endif
signal (SIGPIPE, SIG_IGN);
signal (SIGALRM, SIG_IGN);
signal (SIGTERM, SIG_IGN);
-
- /*
- Make sure that if we opened a server process, we close it now.
- This is the grandparent process. We don't need the server anymore
- and our children should make their own. */
-
- close_server ();
- (void)wait ( (int*)NULL );
}
/* * * * * * * * * * * * *
-
- wait_for_pid - Keep calling `wait(2)' until it returns
- the process id we are looking for. Not every system has
- `waitpid(2)'. We also ensure that the children exit with success. */
-
-void
-wait_for_pid( pid_t child, int file_name_ct )
-{
- for (;;) {
- int status;
- pid_t dead_kid = wait (&status);
-
- if (dead_kid == child)
- {
- if (! WIFEXITED( status ))
- {
- fprintf (stderr, "child process %d is hung on signal %d\n",
- child, WSTOPSIG( status ));
- exit (EXIT_FAILURE);
- }
- if (WEXITSTATUS( status ) != 0)
- {
- fprintf (stderr, "child process %d exited with status %d\n",
- child, WEXITSTATUS( status ));
- exit (EXIT_FAILURE);
- }
- break; /* normal child completion */
- }
- /*
- IF there is an error, THEN see if it is retryable.
- If it is not retryable, then break out of this loop. */
- if (dead_kid == NOPROCESS)
- {
- switch (errno) {
- case EINTR:
- case EAGAIN:
- break;
-
- default:
- fprintf (stderr, "Error %d (%s) waiting for %d to finish\n",
- errno, strerror( errno ), child );
- /* FALLTHROUGH */
-
- case ECHILD: /* no children to wait for?? */
- return;
- }
- }
- } done_waiting:;
-}
-
-
-/* * * * * * * * * * * * *
-
load_file loads all the contents of a file into malloc-ed memory.
Its argument is the name of the file to read in; the returned
result is the NUL terminated contents of the file. The file
is presumed to be an ASCII text file containing no NULs. */
char *
-load_file (pz_file_name)
- const char *pz_file_name;
+load_file ( fname )
+ const char* fname;
{
- char *pz_data;
- size_t file_size;
-
- {
- struct stat stbf;
-
- if (stat (pz_file_name, &stbf) != 0)
- {
- fprintf (stderr, "error %d (%s) stat-ing %s\n",
- errno, strerror (errno), pz_file_name);
- return (char *) NULL;
- }
- file_size = stbf.st_size;
- }
- if (file_size == 0)
- return (char *) NULL;
+ struct stat stbf;
+ char* res;
- pz_data = (char *) malloc ((file_size + 16) & ~0x00F);
- if (pz_data == (char *) NULL)
+ if (stat (fname, &stbf) != 0)
{
- fprintf (stderr, "error: could not malloc %d bytes\n",
- file_size);
- exit (EXIT_FAILURE);
+ if (NOT_SILENT)
+ fprintf (stderr, "error %d (%s) stat-ing %s\n",
+ errno, xstrerror (errno), fname );
+ return (char *) NULL;
}
+ if (stbf.st_size == 0)
+ return (char*)NULL;
- {
- FILE *fp = fopen (pz_file_name, "r");
- size_t size_left = file_size;
- char *read_ptr = pz_data;
-
- if (fp == (FILE *) NULL)
- {
- fprintf (stderr, "error %d (%s) opening %s\n", errno,
- strerror (errno), pz_file_name);
- free ((void *) pz_data);
- return (char *) NULL;
- }
-
- do
- {
- size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp);
-
- if (sizeRead == 0)
- {
- if (feof (fp))
- break;
+ data_map_size = stbf.st_size+1;
+ data_map_fd = open (fname, O_RDONLY);
+ ttl_data_size += data_map_size-1;
- if (ferror (fp))
- {
- int err = errno;
- if (err != EISDIR)
- fprintf (stderr, "error %d (%s) reading %s\n", err,
- strerror (err), pz_file_name);
- free ((void *) pz_data);
- fclose (fp);
- return (char *) NULL;
- }
- }
+ if (data_map_fd < 0)
+ {
+ if (NOT_SILENT)
+ fprintf (stderr, "error %d (%s) opening %s for read\n",
+ errno, xstrerror (errno), fname);
+ return (char*)NULL;
+ }
- read_ptr += sizeRead;
- size_left -= sizeRead;
- }
- while (size_left != 0);
+#ifdef HAVE_MMAP_FILE
+ curr_data_mapped = BOOL_TRUE;
+ res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ, MAP_PRIVATE,
+ data_map_fd, 0);
+ if (res == (char*)BAD_ADDR)
+ {
+ curr_data_mapped = BOOL_FALSE;
+ res = load_file_data ( fdopen (data_map_fd, "r"));
+ }
+#else
+ curr_data_mapped = BOOL_FALSE;
+ res = load_file_data ( fdopen (data_map_fd, "r"));
+#endif
- *read_ptr = '\0';
- fclose (fp);
- }
- return pz_data;
+ return res;
}
/* * * * * * * * * * * * *
-
+
run_compiles run all the regexp compiles for all the fixes once.
*/
void
run_compiles ()
{
- tSCC z_bad_comp[] = "fixincl ERROR: cannot compile %s regex for %s\n\
-\texpr = `%s'\n\terror %s\n";
tFixDesc *p_fixd = fixDescList;
int fix_ct = FIX_COUNT;
tTestDesc *p_test;
int test_ct;
- int re_ct = REGEX_COUNT;
const char *pz_err;
regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
exit (EXIT_FAILURE);
}
- /* Make sure re_compile_pattern does not stumble across invalid
- data */
+ /* Make sure compile_re does not stumble across invalid data */
memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) );
memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) );
- /* The patterns we search for are all egrep patterns.
- In the shell version of this program, we invoke egrep
- with the supplied pattern. Here, we will run
- re_compile_pattern, but it must be using the same rules. */
+ compile_re (incl_quote_pat, &incl_quote_re, 1,
+ "quoted include", "run_compiles");
- re_set_syntax (RE_SYNTAX_EGREP);
- pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1,
- &incl_quote_re);
- if (pz_err != (char *) NULL)
- {
- fprintf (stderr, z_bad_comp, "quoted include", "run_compiles",
- incl_quote_pat, pz_err);
- exit (EXIT_FAILURE);
- }
+ /* Allow machine name tests to be ignored (testing, mainly) */
+
+ if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
+ pz_machine = (char*)NULL;
/* FOR every fixup, ... */
do
if ( (pz_machine != NULL)
&& (p_fixd->papz_machs != (const char**) NULL) )
{
+ tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
+ tSCC esac_fmt[] =
+ " )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
+ tSCC skip[] = "skip"; /* 4 bytes */
+ tSCC run[] = "run"; /* 3 bytes */
+ /* total bytes to add to machine sum: 49 - see fixincl.tpl */
+
const char **papz_machs = p_fixd->papz_machs;
- char *pz = file_name_buf;
+ char *pz;
char *pz_sep = "";
tCC *pz_if_true;
tCC *pz_if_false;
- tSCC skip[] = "skip";
- tSCC run[] = "run";
-
- /* Construct a shell script that looks like this:
+ char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
- case our-cpu-platform-os in
- tests-cpu-platform-os-pattern )
- echo run ;;
- * )
- echo skip ;;
- esac
+ /* Start the case statement */
- where 'run' and 'skip' may be reversed, depending on
- the sense of the test. */
+ sprintf (cmd_buf, case_fmt, pz_machine);
+ pz = cmd_buf + strlen (cmd_buf);
- sprintf (pz, "case %s in\n", pz_machine);
- pz += strlen (pz);
+ /* Determine if a match means to apply the fix or not apply it */
if (p_fixd->fd_flags & FD_MACH_IFNOT)
{
pz_if_false = skip;
}
- /* FOR any additional machine names to test for,
- insert the " | \\\n" glue and the machine pattern. */
+ /* Emit all the machine names. If there are more than one,
+ then we will insert " | \\\n" between the names */
for (;;)
{
if (pz_mach == (const char*) NULL)
break;
- sprintf (pz, "%s %s", pz_sep, pz_mach);
+ sprintf (pz, "%s%s", pz_sep, pz_mach);
pz += strlen (pz);
pz_sep = " | \\\n";
}
- sprintf (pz, " )\n echo %s ;;\n * )\n echo %s ;;\nesac",
- pz_if_true, pz_if_false);
+
+ /* Now emit the match and not-match actions and the esac */
+
+ sprintf (pz, esac_fmt, pz_if_true, pz_if_false);
/* Run the script.
The result will start either with 's' or 'r'. */
{
int skip;
- pz = run_shell (file_name_buf);
+ pz = run_shell (cmd_buf);
skip = (*pz == 's');
free ( (void*)pz );
if (skip)
{
case TT_EGREP:
case TT_NEGREP:
- /* You might consider putting the following under #ifdef.
- The number of re's used is computed by autogen.
- So, it is static and known at compile time. */
-
- if (--re_ct < 0)
- {
- fputs ("out of RE's\n", stderr);
- exit (EXIT_FAILURE);
- }
+#ifdef DEBUG
+ {
+ static int re_ct = REGEX_COUNT;
+ if (--re_ct < 0)
+ {
+ fputs ("out of RE's\n", stderr);
+ exit (EXIT_FAILURE);
+ }
+ }
+#endif
p_test->p_test_regex = p_re++;
- pz_err = re_compile_pattern (p_test->pz_test_text,
- strlen (p_test->pz_test_text),
- p_test->p_test_regex);
- if (pz_err != (char *) NULL)
- {
- fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name,
- p_test->pz_test_text, pz_err);
- exit (EXIT_FAILURE);
- }
+ compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
+ "select test", p_fixd->fix_name);
}
p_test++;
}
/* * * * * * * * * * * * *
-
+
create_file Create the output modified file.
Input: the name of the file to create
Returns: a file pointer to the new, open file */
-#define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#if defined(S_IRUSR) && defined(S_IWUSR) && \
+ defined(S_IRGRP) && defined(S_IROTH)
+
+# define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#else
+# define S_IRALL 0644
+#endif
+
+#if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
+ defined(S_IROTH) && defined(S_IXOTH)
+
+# define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+#else
+# define S_DIRALL 0755
+#endif
+
FILE *
-create_file (pz_file_name)
- const char *pz_file_name;
+create_file ()
{
int fd;
FILE *pf;
char fname[MAXPATHLEN];
-#ifdef DEBUG
- if (strncmp( pz_file_name, pz_find_base, find_base_len ) != 0)
- {
- fprintf (stderr, "Error: input file %s does not match %s/*\n",
- pz_file_name, pz_find_base );
- exit (1);
- }
-#endif
-
- sprintf (fname, "%s/%s", pz_dest_dir, pz_file_name + find_base_len);
+ sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
*pz_dir = NUL;
if (stat (fname, &stbf) < 0)
{
- mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
- | S_IROTH | S_IXOTH);
+ mkdir (fname, S_IFDIR | S_DIRALL);
}
*pz_dir = '/';
if (fd < 0)
{
fprintf (stderr, "Error %d (%s) creating %s\n",
- errno, strerror (errno), fname);
+ errno, xstrerror (errno), fname);
exit (EXIT_FAILURE);
}
- fprintf (stderr, "Fixed: %s\n", pz_file_name);
+ if (NOT_SILENT)
+ fprintf (stderr, "Fixed: %s\n", pz_curr_file);
pf = fdopen (fd, "w");
-#ifdef LATER
- {
- static const char hdr[] =
- "/* DO NOT EDIT THIS FILE.\n\n"
- " It has been auto-edited by fixincludes from /usr/include/%s\n"
- " This had to be done to correct non-standard usages in the\n"
- " original, manufacturer supplied header file. */\n\n";
+ /*
+ * IF pz_machine is NULL, then we are in some sort of test mode.
+ * Do not insert the current directory name. Use a constant string.
+ */
+ fprintf (pf, z_std_preamble,
+ (pz_machine == NULL)
+ ? "fixinc/tests/inc"
+ : pz_input_dir,
+ pz_curr_file);
- fprintf (pf, hdr, pz_file_name);
- }
-#endif
return pf;
}
test_test make sure a shell-style test expression passes.
Input: a pointer to the descriptor of the test to run and
the name of the file that we might want to fix
- Result: SUCCESS or FAILURE, depending on the result of the
+ Result: APPLY_FIX or SKIP_FIX, depending on the result of the
shell script we run. */
-t_success
-test_test (p_test, pz_file_name)
+int
+test_test (p_test, pz_test_file)
tTestDesc *p_test;
- char* pz_file_name;
+ char* pz_test_file;
{
tSCC cmd_fmt[] =
"file=%s\n\
fi";
char *pz_res;
- t_success res = FAILURE;
+ int res;
static char cmd_buf[4096];
- sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text);
+ sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
pz_res = run_shell (cmd_buf);
- if (*pz_res == 'T')
- res = SUCCESS;
+
+ switch (*pz_res) {
+ case 'T':
+ res = APPLY_FIX;
+ break;
+
+ case 'F':
+ res = SKIP_FIX;
+ break;
+
+ default:
+ fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
+ pz_res, cmd_buf );
+ }
+
free ((void *) pz_res);
return res;
}
/* * * * * * * * * * * * *
-
+
egrep_test make sure an egrep expression is found in the file text.
Input: a pointer to the descriptor of the test to run and
the pointer to the contents of the file under suspicion
- Result: SUCCESS if the pattern is found, FAILURE otherwise
+ Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
- The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test
+ The caller may choose to reverse meaning if the sense of the test
is inverted. */
-t_success
+int
egrep_test (pz_data, p_test)
char *pz_data;
tTestDesc *p_test;
{
- regmatch_t match;
-
-#ifndef NO_BOGOSITY
+#ifdef DEBUG
if (p_test->p_test_regex == 0)
fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
p_test->pz_test_text);
#endif
- if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0)
- return SUCCESS;
- return FAILURE;
+ if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
+ return APPLY_FIX;
+ return SKIP_FIX;
}
for (;;) {
char ch = *pz_file++;
- if (! isgraph( ch ))
+ if (! ISGRAPH( ch ))
return 0;
if (ch == '"')
break;
/* * * * * * * * * * * * *
*
extract_quoted_files
-
+
The syntax, `#include "file.h"' specifies that the compiler is to
search the local directory of the current file before the include
list. Consequently, if we have modified a header and stored it in
file in that fashion must also be copied into this new directory.
This routine finds those flavors of #include and for each one found
emits a triple of:
-
+
1. source directory of the original file
2. the relative path file name of the #includ-ed file
3. the full destination path for this file
void
-extract_quoted_files (pz_data, pz_file_name, p_re_match)
+extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
char *pz_data;
- const char *pz_file_name;
+ const char *pz_fixed_file;
regmatch_t *p_re_match;
{
- char *pz_dir_end = strrchr (pz_file_name, '/');
+ char *pz_dir_end = strrchr (pz_fixed_file, '/');
char *pz_incl_quot = pz_data;
- fprintf (stderr, "Quoted includes in %s\n", pz_file_name);
+ if (VLEVEL( VERB_APPLIES ))
+ fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
- /* Set "pz_file_name" to point to the containing subdirectory of the source
+ /* Set "pz_fixed_file" to point to the containing subdirectory of the source
If there is none, then it is in our current directory, ".". */
if (pz_dir_end == (char *) NULL)
- pz_file_name = ".";
+ pz_fixed_file = ".";
else
*pz_dir_end = '\0';
pz_incl_quot += p_re_match->rm_so;
/* Skip forward to the included file name */
- while (isspace (*pz_incl_quot))
+ while (ISSPACE (*pz_incl_quot))
pz_incl_quot++;
- while (isspace (*++pz_incl_quot))
+ /* ISSPACE() may evaluate is argument more than once! */
+ while (++pz_incl_quot, ISSPACE (*pz_incl_quot))
;
pz_incl_quot += sizeof ("include") - 1;
while (*pz_incl_quot++ != '"')
;
- if (quoted_file_exists (pz_src_dir, pz_file_name, pz_incl_quot))
+ if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
{
/* Print the source directory and the subdirectory
of the file in question. */
- printf ("%s %s/", pz_src_dir, pz_file_name);
+ printf ("%s %s/", pz_src_dir, pz_fixed_file);
pz_dir_end = pz_incl_quot;
/* Append to the directory the relative path of the desired file */
/* Now print the destination directory appended with the
relative path of the desired file */
- printf (" %s/%s/", pz_dest_dir, pz_file_name);
+ printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
while (*pz_dir_end != '"')
putc (*pz_dir_end++, stdout);
/* * * * * * * * * * * * *
- Process the potential fixes for a particular include file.
- Input: the original text of the file and the file's name
- Result: none. A new file may or may not be created. */
+ Somebody wrote a *_fix subroutine that we must call.
+ */
-void
-process (pz_data, pz_file_name)
- char *pz_data;
- const char *pz_file_name;
+int
+internal_fix (read_fd, p_fixd)
+ int read_fd;
+ tFixDesc* p_fixd;
{
- static char env_current_file[1024] = { "file=" };
- tFixDesc *p_fixd = fixDescList;
- int todo_ct = FIX_COUNT;
- t_fd_pair fdp = { -1, -1 };
+ int fd[2];
+
+ if (pipe( fd ) != 0)
+ {
+ fprintf (stderr, "Error %d on pipe(2) call\n", errno );
+ exit (EXIT_FAILURE);
+ }
+
+ for (;;)
+ {
+ pid_t childid = fork();
+
+ switch (childid)
+ {
+ case -1:
+ break;
+
+ case 0:
+ close (fd[0]);
+ goto do_child_task;
+
+ default:
+ /*
+ * Parent process
+ */
+ close (read_fd);
+ close (fd[1]);
+ return fd[0];
+ }
- /* IF this is the first time through,
- THEN put the 'file' environment variable into the environment.
- This is used by some of the subject shell scripts and tests. */
+ /*
+ * Parent in error
+ */
+ fprintf (stderr, z_fork_err, errno, xstrerror (errno),
+ p_fixd->fix_name);
+ {
+ static int failCt = 0;
+ if ((errno != EAGAIN) || (++failCt > 10))
+ exit (EXIT_FAILURE);
+ sleep (1);
+ }
+ } do_child_task:;
- if (env_current_file[5] == NUL)
- putenv (env_current_file);
+ /*
+ * Close our current stdin and stdout
+ */
+ close (STDIN_FILENO);
+ close (STDOUT_FILENO);
+ UNLOAD_DATA();
/*
- Ghastly as it is, this actually updates the value of the variable:
-
- putenv(3C) C Library Functions putenv(3C)
-
- DESCRIPTION
- putenv() makes the value of the environment variable name
- equal to value by altering an existing variable or creating
- a new one. In either case, the string pointed to by string
- becomes part of the environment, so altering the string will
- change the environment. string points to a string of the
- form ``name=value.'' The space used by string is no longer
- used once a new string-defining name is passed to putenv().
+ * Make the fd passed in the stdin, and the write end of
+ * the new pipe become the stdout.
*/
- strcpy (env_current_file + 5, pz_file_name);
- process_chain_head = NOPROCESS;
- fprintf (stderr, "%-50s \r", pz_file_name );
- /* For every fix in our fix list, ... */
- for (; todo_ct > 0; p_fixd++, todo_ct--)
+ fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
+ fcntl (read_fd, F_DUPFD, STDIN_FILENO);
+
+ apply_fix (p_fixd, pz_curr_file);
+ exit (0);
+}
+
+
+/* * * * * * * * * * * * *
+
+ This loop should only cycle for 1/2 of one loop.
+ "chain_open" starts a process that uses "read_fd" as
+ its stdin and returns the new fd this process will use
+ for stdout. */
+
+int
+start_fixer (read_fd, p_fixd, pz_fix_file)
+ int read_fd;
+ tFixDesc* p_fixd;
+ char* pz_fix_file;
+{
+ tCC* pz_cmd_save;
+ char* pz_cmd;
+
+ if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
+ return internal_fix (read_fd, p_fixd);
+
+ if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
+ pz_cmd = (char*)NULL;
+ else
{
- tTestDesc *p_test;
- int test_ct;
+ tSCC z_cmd_fmt[] = "file='%s'\n%s";
+ pz_cmd = (char*)malloc (strlen (p_fixd->patch_args[2])
+ + sizeof( z_cmd_fmt )
+ + strlen( pz_fix_file ));
+ if (pz_cmd == (char*)NULL)
+ {
+ fputs ("allocation failure\n", stderr);
+ exit (EXIT_FAILURE);
+ }
+ sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
+ pz_cmd_save = p_fixd->patch_args[2];
+ p_fixd->patch_args[2] = pz_cmd;
+ }
- if (p_fixd->fd_flags & FD_SKIP_TEST)
- continue;
+ /* Start a fix process, handing off the previous read fd for its
+ stdin and getting a new fd that reads from the fix process' stdout.
+ We normally will not loop, but we will up to 10 times if we keep
+ getting "EAGAIN" errors.
- /* IF there is a file name restriction,
- THEN ensure the current file name matches one in the pattern */
+ */
+ for (;;)
+ {
+ static int failCt = 0;
+ int fd;
- if (p_fixd->file_list != (char *) NULL)
+ fd = chain_open (read_fd,
+ (t_pchar *) p_fixd->patch_args,
+ (process_chain_head == -1)
+ ? &process_chain_head : (pid_t *) NULL);
+
+ if (fd != -1)
{
- const char *pz_fname = pz_file_name;
- const char *pz_scan = p_fixd->file_list;
- size_t name_len;
+ read_fd = fd;
+ break;
+ }
- while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
- pz_fname += 2;
- name_len = strlen (pz_fname);
+ fprintf (stderr, z_fork_err, errno, xstrerror (errno),
+ p_fixd->fix_name);
- for (;;)
- {
- pz_scan = strstr (pz_scan + 1, pz_fname);
- /* IF we can't match the string at all,
- THEN bail */
- if (pz_scan == (char *) NULL)
- goto next_fix;
+ if ((errno != EAGAIN) || (++failCt > 10))
+ exit (EXIT_FAILURE);
+ sleep (1);
+ }
- /* IF the match is surrounded by the '|' markers,
- THEN we found a full match -- time to run the tests */
+ /* IF we allocated a shell script command,
+ THEN free it and restore the command format to the fix description */
+ if (pz_cmd != (char*)NULL)
+ {
+ free ((void*)pz_cmd);
+ p_fixd->patch_args[2] = pz_cmd_save;
+ }
- if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
- break;
- }
+ return read_fd;
+}
+
+
+/* * * * * * * * * * * * *
+
+ Process the potential fixes for a particular include file.
+ Input: the original text of the file and the file's name
+ Result: none. A new file may or may not be created. */
+
+t_bool
+fix_applies (p_fixd)
+ tFixDesc *p_fixd;
+{
+#ifdef DEBUG
+ static const char z_failed[] = "not applying %s %s to %s - \
+test %d failed\n";
+#endif
+ const char *pz_fname = pz_curr_file;
+ const char *pz_scan = p_fixd->file_list;
+ int test_ct;
+ tTestDesc *p_test;
+
+ if (p_fixd->fd_flags & FD_SKIP_TEST)
+ return BOOL_FALSE;
+
+ /* IF there is a file name restriction,
+ THEN ensure the current file name matches one in the pattern */
+
+ if (pz_scan != (char *) NULL)
+ {
+ size_t name_len;
+
+ while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
+ pz_fname += 2;
+ name_len = strlen (pz_fname);
+
+ for (;;)
+ {
+ pz_scan = strstr (pz_scan + 1, pz_fname);
+ /* IF we can't match the string at all,
+ THEN bail */
+ if (pz_scan == (char *) NULL) {
+#ifdef DEBUG
+ if (VLEVEL( VERB_EVERYTHING ))
+ fprintf (stderr, "file %s not in list for %s\n",
+ pz_fname, p_fixd->fix_name );
+#endif
+ return BOOL_FALSE;
+ }
+
+ /* IF the match is surrounded by the '|' markers,
+ THEN we found a full match -- time to run the tests */
+
+ if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|'))
+ break;
}
+ }
- /* FOR each test, see if it fails.
- IF it does fail, then we go on to the next test */
+ /* FOR each test, see if it fails.
+ IF it does fail, then we go on to the next test */
- for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
- test_ct-- > 0;
- p_test++)
+ for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
+ test_ct-- > 0;
+ p_test++)
+ {
+ switch (p_test->type)
{
-#ifdef DEBUG_TEST
- static const char z_test_fail[] =
- "%16s test %2d failed for %s\n";
+ case TT_TEST:
+ if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
+#ifdef DEBUG
+ if (VLEVEL( VERB_EVERYTHING ))
+ fprintf (stderr, z_failed, "TEST", p_fixd->fix_name,
+ pz_fname, p_fixd->test_ct - test_ct);
#endif
- switch (p_test->type)
- {
- case TT_TEST:
- if (!SUCCESSFUL (test_test (p_test, pz_file_name)))
- {
-#ifdef DEBUG_TEST
- fprintf (stderr, z_test_fail, p_fixd->fix_name,
- p_fixd->test_ct - test_ct, pz_file_name);
+ return BOOL_FALSE;
+ }
+ break;
+
+ case TT_EGREP:
+ if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
+#ifdef DEBUG
+ if (VLEVEL( VERB_EVERYTHING ))
+ fprintf (stderr, z_failed, "EGREP", p_fixd->fix_name,
+ pz_fname, p_fixd->test_ct - test_ct);
#endif
- goto next_fix;
- }
- break;
+ return BOOL_FALSE;
+ }
+ break;
- case TT_EGREP:
- if (!SUCCESSFUL (egrep_test (pz_data, p_test)))
- {
-#ifdef DEBUG_TEST
- fprintf (stderr, z_test_fail, p_fixd->fix_name,
- p_fixd->test_ct - test_ct, pz_file_name);
+ case TT_NEGREP:
+ if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
+#ifdef DEBUG
+ if (VLEVEL( VERB_EVERYTHING ))
+ fprintf (stderr, z_failed, "NEGREP", p_fixd->fix_name,
+ pz_fname, p_fixd->test_ct - test_ct);
#endif
- goto next_fix;
- }
- break;
+ /* Negated sense */
+ return BOOL_FALSE;
+ }
+ break;
- case TT_NEGREP:
- if (SUCCESSFUL (egrep_test (pz_data, p_test)))
- {
-#ifdef DEBUG_TEST
- fprintf (stderr, z_test_fail, p_fixd->fix_name,
- p_fixd->test_ct - test_ct, pz_file_name);
+ case TT_FUNCTION:
+ if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
+ != APPLY_FIX) {
+#ifdef DEBUG
+ if (VLEVEL( VERB_EVERYTHING ))
+ fprintf (stderr, z_failed, "FTEST", p_fixd->fix_name,
+ pz_fname, p_fixd->test_ct - test_ct);
#endif
- goto next_fix;
- }
- break;
- }
+ return BOOL_FALSE;
+ }
+ break;
}
+ }
- fprintf (stderr, "Applying %-24s to %s\n",
- p_fixd->fix_name, pz_file_name);
+ return BOOL_TRUE;
+}
- /* IF we do not have a read pointer,
- THEN this is the first fix for the current file.
- Open the source file. That will be used as stdin for
- the first fix. Any subsequent fixes will use the
- stdout descriptor of the previous fix as its stdin. */
- if (fdp.read_fd == -1)
- {
- fdp.read_fd = open (pz_file_name, O_RDONLY);
- if (fdp.read_fd < 0)
- {
- fprintf (stderr, "Error %d (%s) opening %s\n", errno,
- strerror (errno), pz_file_name);
- exit (EXIT_FAILURE);
- }
- }
+/* * * * * * * * * * * * *
- /* This loop should only cycle for 1/2 of one loop.
- "chain_open" starts a process that uses "fdp.read_fd" as
- its stdin and returns the new fd this process will use
- for stdout. */
+ Write out a replacement file */
- for (;;)
+void
+write_replacement (p_fixd)
+ tFixDesc *p_fixd;
+{
+ const char* pz_text = p_fixd->patch_args[0];
+
+ if ((pz_text == (char*)NULL) || (*pz_text == NUL))
+ return;
+
+ {
+ FILE* out_fp = create_file (pz_curr_file);
+ fputs (pz_text, out_fp);
+ fclose (out_fp);
+ }
+}
+
+
+/* * * * * * * * * * * * *
+
+ We have work to do. Read back in the output
+ of the filtering chain. Compare each byte as we read it with
+ the contents of the original file. As soon as we find any
+ difference, we will create the output file, write out all
+ the matched text and then copy any remaining data from the
+ output of the filter chain.
+ */
+void
+test_for_changes (read_fd)
+ int read_fd;
+{
+ FILE *in_fp = fdopen (read_fd, "r");
+ FILE *out_fp = (FILE *) NULL;
+ char *pz_cmp = pz_curr_data;
+
+#ifdef DO_STATS
+ fixed_ct++;
+#endif
+ for (;;)
+ {
+ int ch;
+
+ ch = getc (in_fp);
+ if (ch == EOF)
+ break;
+
+ /* IF we are emitting the output
+ THEN emit this character, too.
+ */
+ if (out_fp != (FILE *) NULL)
+ putc (ch, out_fp);
+
+ /* ELSE if this character does not match the original,
+ THEN now is the time to start the output.
+ */
+ else if (ch != *pz_cmp)
{
- tSCC z_err[] = "Error %d (%s) starting filter process for %s\n";
- static int failCt = 0;
- int fd = chain_open (fdp.read_fd,
- (t_pchar *) p_fixd->patch_args,
- (process_chain_head == -1)
- ? &process_chain_head : (pid_t *) NULL);
-
- if (fd != -1)
- {
- fdp.read_fd = fd;
- break;
- }
+ out_fp = create_file (pz_curr_file);
- fprintf (stderr, z_err, errno, strerror (errno),
- p_fixd->fix_name);
+#ifdef DO_STATS
+ altered_ct++;
+#endif
+ /* IF there are matched data, write the matched part now. */
+ if (pz_cmp != pz_curr_data)
+ fwrite (pz_curr_data, (size_t)(pz_cmp - pz_curr_data), 1, out_fp);
- if ((errno != EAGAIN) || (++failCt > 10))
- exit (EXIT_FAILURE);
- sleep (1);
+ /* Emit the current unmatching character */
+ putc (ch, out_fp);
}
+ else
+ /* ELSE the character matches. Advance the compare ptr */
+ pz_cmp++;
+ }
+
+ /* IF we created the output file, ... */
+ if (out_fp != (FILE *) NULL)
+ {
+ regmatch_t match;
- next_fix:
- ;
+ /* Close the file and see if we have to worry about
+ `#include "file.h"' constructs. */
+ fclose (out_fp);
+ if (regexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
+ extract_quoted_files (pz_curr_data, pz_curr_file, &match);
}
- /* IF after all the tests we did not start any patch programs,
- THEN quit now. */
+ fclose (in_fp);
+ close (read_fd); /* probably redundant, but I'm paranoid */
+}
+
+
+/* * * * * * * * * * * * *
+
+ Process the potential fixes for a particular include file.
+ Input: the original text of the file and the file's name
+ Result: none. A new file may or may not be created. */
+
+void
+process ()
+{
+ static char env_current_file[1024];
+ tFixDesc *p_fixd = fixDescList;
+ int todo_ct = FIX_COUNT;
+ int read_fd = -1;
+ int num_children = 0;
- if (fdp.read_fd < 0)
+ if (access (pz_curr_file, R_OK) != 0)
+ {
+ int erno = errno;
+ fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
+ pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
+ erno, xstrerror (erno));
+ return;
+ }
+
+ pz_curr_data = load_file (pz_curr_file);
+ if (pz_curr_data == (char *) NULL)
return;
- /* OK. We have work to do. Read back in the output
- of the filtering chain. Compare each byte as we read it with
- the contents of the original file. As soon as we find any
- difference, we will create the output file, write out all
- the matched text and then copy any remaining data from the
- output of the filter chain.
- */
- {
- FILE *in_fp = fdopen (fdp.read_fd, "r");
- FILE *out_fp = (FILE *) NULL;
- char *pz_cmp = pz_data;
+#ifdef DO_STATS
+ process_ct++;
+#endif
+ if (VLEVEL( VERB_PROGRESS ) && have_tty)
+ fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
- for (;;)
- {
- int ch;
+ process_chain_head = NOPROCESS;
- ch = getc (in_fp);
- if (ch == EOF)
- break;
+ /* For every fix in our fix list, ... */
+ for (; todo_ct > 0; p_fixd++, todo_ct--)
+ {
+ if (! fix_applies (p_fixd))
+ continue;
- /* IF we are emitting the output
- THEN emit this character, too.
- */
- if (out_fp != (FILE *) NULL)
- putc (ch, out_fp);
+ if (VLEVEL( VERB_APPLIES ))
+ fprintf (stderr, "Applying %-24s to %s\n",
+ p_fixd->fix_name, pz_curr_file);
- /* ELSE if this character does not match the original,
- THEN now is the time to start the output.
- */
- else if (ch != *pz_cmp)
- {
- out_fp = create_file (pz_file_name);
+ if (p_fixd->fd_flags & FD_REPLACEMENT)
+ {
+ write_replacement (p_fixd);
+ UNLOAD_DATA();
+ return;
+ }
- /* IF there are matched data, write it all now. */
- if (pz_cmp != pz_data)
- {
- char c = *pz_cmp;
-
- *pz_cmp = NUL;
- fputs (pz_data, out_fp);
- *pz_cmp = c;
- }
+ /* IF we do not have a read pointer,
+ THEN this is the first fix for the current file.
+ Open the source file. That will be used as stdin for
+ the first fix. Any subsequent fixes will use the
+ stdout descriptor of the previous fix for its stdin. */
- /* Emit the current unmatching character */
- putc (ch, out_fp);
- }
- else
- /* ELSE the character matches. Advance the compare ptr */
- pz_cmp++;
- }
+ if (read_fd == -1)
+ {
+ read_fd = open (pz_curr_file, O_RDONLY);
+ if (read_fd < 0)
+ {
+ fprintf (stderr, "Error %d (%s) opening %s\n", errno,
+ xstrerror (errno), pz_curr_file);
+ exit (EXIT_FAILURE);
+ }
- /* IF we created the output file, ... */
- if (out_fp != (FILE *) NULL)
- {
- regmatch_t match;
+ /* Ensure we do not get duplicate output */
- /* Close the file and see if we have to worry about
- `#include "file.h"' constructs. */
- fclose (out_fp);
- if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0)
- extract_quoted_files (pz_data, pz_file_name, &match);
- }
- fclose (in_fp);
- }
- close (fdp.read_fd); /* probably redundant, but I'm paranoid */
+ fflush (stdout);
+ }
+
+ read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
+ num_children++;
+ }
+
+ /* IF we have a read-back file descriptor,
+ THEN check for changes and write output if changed. */
+
+ if (read_fd >= 0)
+ {
+ test_for_changes (read_fd);
+#ifdef DO_STATS
+ apply_ct += num_children;
+#endif
+ /* Wait for child processes created by chain_open()
+ to avoid leaving zombies. */
+ do {
+ wait ((int *) NULL);
+ } while (--num_children > 0);
+ }
+
+ UNLOAD_DATA();
}