-
/* Install modified versions of certain ANSI-incompatible system header
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.
#include "fixlib.h"
-#if HAVE_MMAP
+#if defined( HAVE_MMAP_FILE )
#include <sys/mman.h>
#define BAD_ADDR ((void*)-1)
#endif
#include <signal.h>
-
+#if ! defined( SIGCHLD ) && defined( SIGCLD )
+# define SIGCHLD SIGCLD
+#endif
+#ifndef SEPARATE_FIX_PROC
#include "server.h"
-
-/* Quality Assurance Marker :-)
-
- Any file that contains this string is presumed to have
- been carefully constructed and will not be fixed */
-
-static const char gnu_lib_mark[] =
- "This file is part of the GNU C Library";
+#endif
/* The contents of this string are not very important. It is mostly
just used as part of the "I am alive and working" test. */
static const char program_id[] = "fixincl version 1.1";
-/* 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, TT_FUNCTION
-} te_test_type;
-
-typedef struct test_desc tTestDesc;
+/* This format will be used at the start of every generated file */
-struct test_desc
-{
- te_test_type type;
- const char *pz_test_text;
- regex_t *p_test_regex;
-};
+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 patch_desc tPatchDesc;
+/* Working environment strings. Essentially, invocation 'options'. */
-/* Fix Descriptor
+#define _ENV_(v,m,n,t) tCC* v = NULL;
+ENV_TABLE
+#undef _ENV_
- 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...)
+int find_base_len = 0;
- NB: the FD_ defines are BIT FLAGS
+typedef enum {
+ VERB_SILENT = 0,
+ VERB_FIXES,
+ VERB_APPLIES,
+ VERB_PROGRESS,
+ VERB_TESTS,
+ VERB_EVERYTHING
+} te_verbose;
- */
-#define FD_MACH_ONLY 0x0000
-#define FD_MACH_IFNOT 0x0001
-#define FD_SHELL_SCRIPT 0x0002
-#define FD_SUBROUTINE 0x0004
-#define FD_REPLACEMENT 0x0008
-#define FD_SKIP_TEST 0x8000
-
-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;
-};
+te_verbose verbose_level = VERB_PROGRESS;
+int have_tty = 0;
-/* Working environment strings. Essentially, invocation 'options'. */
-char *pz_dest_dir = NULL;
-char *pz_src_dir = NULL;
-char *pz_machine = NULL;
-int find_base_len = 0;
+#define VLEVEL(l) ((unsigned int) verbose_level >= (unsigned int) l)
+#define NOT_SILENT VLEVEL(VERB_FIXES)
pid_t process_chain_head = (pid_t) -1;
char* pz_curr_file; /* name of the current file under test/fix */
char* pz_curr_data; /* original contents of that file */
+char* pz_temp_file; /* for DOS, a place to stash the temporary
+ fixed data between system(3) calls */
t_bool curr_data_mapped;
int data_map_fd;
size_t data_map_size;
size_t ttl_data_size = 0;
+
#ifdef DO_STATS
int process_ct = 0;
int apply_ct = 0;
int altered_ct = 0;
#endif /* DO_STATS */
-#ifdef HAVE_MMAP
-#define UNLOAD_DATA() do { if (curr_data_mapped) { \
- munmap ((void*)pz_curr_data, data_map_size); close (data_map_fd); } \
- else free ((void*)pz_curr_data); } while(0)
-#else
-#define UNLOAD_DATA() free ((void*)pz_curr_data)
-#endif
-
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 initialize ();
-void process ();
+static void do_version PARAMS((void)) ATTRIBUTE_NORETURN;
+char *load_file PARAMS((const char *));
+void run_compiles PARAMS((void));
+void initialize PARAMS((int argc,char** argv));
+void process PARAMS((void));
/* External Source Code */
#include "fixincl.x"
-#include "fixtests.c"
-#include "fixfixes.c"
/* * * * * * * * * * * * * * * * * * *
*
* MAIN ROUTINE
*/
+extern int main PARAMS ((int, char **));
int
main (argc, argv)
int argc;
{
char *file_name_buf;
- switch (argc)
- {
- case 1:
- break;
+ initialize ( argc, argv );
- 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, strerror (errno), argv[1] );
- exit (EXIT_FAILURE);
- }
- break;
-
- default:
- fputs ("fixincl ERROR: too many command line arguments\n", stderr);
- exit (EXIT_FAILURE);
- }
-
- initialize ();
+ have_tty = isatty (fileno (stderr));
/* Before anything else, ensure we can allocate our file name buffer. */
file_name_buf = load_file_data (stdin);
continue;
*pz_end = NUL;
-#ifdef NO_BOGOSITY
process ();
-#else
- /* Prevent duplicate output by child process */
-
- fflush (stdout);
- fflush (stderr);
-
- {
- void wait_for_pid _P_(( pid_t ));
- pid_t child = fork ();
- if (child == NULLPROCESS)
- {
- process ();
- return EXIT_SUCCESS;
- }
-
- if (child == NOPROCESS)
- {
- fprintf (stderr, "Error %d (%s) forking in main\n",
- errno, strerror (errno));
- exit (EXIT_FAILURE);
- }
-
- wait_for_pid( child );
- }
-#endif
} /* for (;;) */
#ifdef DO_STATS
- {
+ if (VLEVEL( VERB_PROGRESS )) {
tSCC zFmt[] =
"\
Processed %5d files containing %d bytes \n\
fixed_ct, altered_ct);
}
#endif /* DO_STATS */
- return EXIT_SUCCESS;
+
+# ifdef SEPARATE_FIX_PROC
+ unlink( pz_temp_file );
+# endif
+ exit (EXIT_SUCCESS);
}
-void
+static void
do_version ()
{
static const char zFmt[] = "echo '%s'";
/* 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.
+ 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);
- fputs (zBuf + 5, stdout);
+#ifndef SEPARATE_FIX_PROC
+ puts (zBuf + 5);
exit (strcmp (run_shell (zBuf), program_id));
+#else
+ exit (system (zBuf));
+#endif
}
/* * * * * * * * * * * * */
void
-initialize ()
+initialize ( argc, argv )
+ int argc;
+ char** argv;
{
static const char var_not_found[] =
- "fixincl ERROR: %s environment variable not defined\n\
-\tTARGET_MACHINE, DESTDIR, SRCDIR and FIND_BASE are required\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";
- char *pz = getenv (var);
- if (pz == (char *) NULL)
- {
- fprintf (stderr, var_not_found, var);
- exit (EXIT_FAILURE);
- }
- while ((pz[0] == '.') && (pz[1] == '/'))
- pz += 2;
- if ((pz[0] != '.') || (pz[1] != NUL))
- find_base_len = strlen( pz );
- }
+ default:
+ fputs ("fixincl ERROR: too many command line arguments\n", stderr);
+ exit (EXIT_FAILURE);
+ }
+
+#ifdef SIGCHLD
+ /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
+ receive the signal. A different setting is inheritable */
+ signal (SIGCHLD, SIG_DFL);
+#endif
+
+#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 ();
+# ifdef SEPARATE_FIX_PROC
+ /* NULL as the first argument to `tempnam' causes it to DTRT
+ wrt the temporary directory where the file will be created. */
+ pz_temp_file = tempnam( NULL, "fxinc" );
+# endif
+
signal (SIGQUIT, SIG_IGN);
+#ifdef SIGIOT
signal (SIGIOT, SIG_IGN);
+#endif
+#ifdef SIGPIPE
signal (SIGPIPE, SIG_IGN);
+#endif
signal (SIGALRM, SIG_IGN);
signal (SIGTERM, SIG_IGN);
-#ifndef NO_BOGOSITY
- /*
- 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 );
-#endif
}
-#ifndef NO_BOGOSITY
-/* * * * * * * * * * * * *
-
- 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(child)
- pid_t child;
-{
- 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:;
-}
-#endif /* NO_BOGOSITY */
-
/* * * * * * * * * * * * *
load_file loads all the contents of a file into malloc-ed memory.
if (stat (fname, &stbf) != 0)
{
- fprintf (stderr, "error %d (%s) stat-ing %s\n",
- errno, strerror (errno), fname );
+ 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;
+ /* Make the data map size one larger than the file size for documentation
+ purposes. Truth is that there will be a following NUL character if
+ the file size is not a multiple of the page size. If it is a multiple,
+ then this adjustment sometimes fails anyway. */
data_map_size = stbf.st_size+1;
data_map_fd = open (fname, O_RDONLY);
ttl_data_size += data_map_size-1;
if (data_map_fd < 0)
{
- fprintf (stderr, "error %d (%s) opening %s for read\n",
- errno, strerror (errno), fname);
+ if (NOT_SILENT)
+ fprintf (stderr, "error %d (%s) opening %s for read\n",
+ errno, xstrerror (errno), fname);
return (char*)NULL;
}
-#ifdef HAVE_MMAP
+#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 the file size is a multiple of the page size,
+ THEN sometimes you will seg fault trying to access a trailing byte */
+ if ((stbf.st_size & (getpagesize()-1)) == 0)
+ res = (char*)BAD_ADDR;
+ else
+ res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
+ MAP_PRIVATE, data_map_fd, 0);
if (res == (char*)BAD_ADDR)
+#endif
{
+ FILE* fp = fdopen (data_map_fd, "r");
curr_data_mapped = BOOL_FALSE;
- res = load_file_data ( fdopen (data_map_fd, "r"));
+ res = load_file_data (fp);
+ fclose (fp);
}
-#else
- curr_data_mapped = BOOL_FALSE;
- res = load_file_data ( fdopen (data_map_fd, "r"));
-#endif
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));
-
- if (p_re == (regex_t *) NULL)
- {
- fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n",
- REGEX_COUNT * sizeof (regex_t));
- exit (EXIT_FAILURE);
- }
-
- /* Make sure re_compile_pattern 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. */
-
- 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);
- }
-
- /* FOR every fixup, ... */
- do
- {
- p_test = p_fixd->p_test_desc;
- test_ct = p_fixd->test_ct;
-
- /* IF the machine type pointer is not NULL (we are not in test mode)
- AND this test is for or not done on particular machines
- THEN ... */
-
- if ( (pz_machine != NULL)
- && (p_fixd->papz_machs != (const char**) NULL) )
+static int machine_matches PARAMS ((tFixDesc *));
+static int
+machine_matches( p_fixd )
+ tFixDesc *p_fixd;
{
+# ifndef SEPARATE_FIX_PROC
tSCC case_fmt[] = "case %s in\n"; /* 9 bytes, plus string */
tSCC esac_fmt[] =
" )\n echo %s ;;\n* ) echo %s ;;\nesac";/* 4 bytes */
const char **papz_machs = p_fixd->papz_machs;
char *pz;
- char *pz_sep = "";
+ const char *pz_sep = "";
tCC *pz_if_true;
tCC *pz_if_false;
char cmd_buf[ MACH_LIST_SIZE_LIMIT ]; /* size lim from fixincl.tpl */
if (skip)
{
p_fixd->fd_flags |= FD_SKIP_TEST;
- continue;
- }
- }
- }
+ return BOOL_FALSE;
+ }
+ }
+
+ return BOOL_TRUE;
+# else /* is SEPARATE_FIX_PROC */
+ const char **papz_machs = p_fixd->papz_machs;
+ int invert = (p_fixd->fd_flags & FD_MACH_IFNOT) != 0;
+ for (;;)
+ {
+ const char* pz_mach = *(papz_machs++);
+
+ if (pz_mach == (const char*) NULL)
+ break;
+ if (strstr (pz_mach, "dos") != NULL && !invert)
+ return BOOL_TRUE;
+ }
+
+ p_fixd->fd_flags |= FD_SKIP_TEST;
+ return BOOL_FALSE;
+# endif
+}
+
+/* * * * * * * * * * * * *
+
+ run_compiles run all the regexp compiles for all the fixes once.
+ */
+void
+run_compiles ()
+{
+ tFixDesc *p_fixd = fixDescList;
+ int fix_ct = FIX_COUNT;
+ regex_t *p_re = (regex_t *) xmalloc (REGEX_COUNT * sizeof (regex_t));
+
+ /* 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) );
+
+ compile_re (incl_quote_pat, &incl_quote_re, 1,
+ "quoted include", "run_compiles");
+
+ /* 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
+ {
+ tTestDesc *p_test = p_fixd->p_test_desc;
+ int test_ct = p_fixd->test_ct;
+
+ /* IF the machine type pointer is not NULL (we are not in test mode)
+ AND this test is for or not done on particular machines
+ THEN ... */
+
+ if ( (pz_machine != NULL)
+ && (p_fixd->papz_machs != (const char**) NULL)
+ && ! machine_matches (p_fixd) )
+ continue;
/* FOR every test for the fixup, ... */
{
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);
- }
-
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);
+ default: break;
}
p_test++;
}
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 *
+static FILE *create_file PARAMS ((void));
+static FILE *
create_file ()
{
int fd;
*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_curr_file);
+ 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_curr_file);
- }
-#endif
return pf;
}
the name of the file that we might want to fix
Result: APPLY_FIX or SKIP_FIX, depending on the result of the
shell script we run. */
-
-int
+#ifndef SEPARATE_FIX_PROC
+static int test_test PARAMS ((tTestDesc *, char *));
+static int
test_test (p_test, pz_test_file)
tTestDesc *p_test;
char* pz_test_file;
fi";
char *pz_res;
- int res = SKIP_FIX;
+ int res;
static char cmd_buf[4096];
sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
pz_res = run_shell (cmd_buf);
- if (*pz_res == 'T')
+
+ 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;
}
-
+#else
+/*
+ * IF we are in MS-DOS land, then whatever shell-type test is required
+ * will, by definition, fail
+ */
+#define test_test(t,tf) SKIP_FIX
+#endif
/* * * * * * * * * * * * *
The caller may choose to reverse meaning if the sense of the test
is inverted. */
-int
+static int egrep_test PARAMS ((char *, tTestDesc *));
+static int
egrep_test (pz_data, p_test)
char *pz_data;
tTestDesc *p_test;
{
- regmatch_t match;
-
#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)
+ if (regexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
return APPLY_FIX;
return SKIP_FIX;
}
the file name. If we emit the name, our invoking shell will try
to copy a non-existing file into the destination directory. */
-int
+static int quoted_file_exists PARAMS ((const char *, const char *, const char *));
+static int
quoted_file_exists (pz_src_path, pz_file_path, pz_file)
- char* pz_src_path;
- char* pz_file_path;
- char* pz_file;
+ const char *pz_src_path;
+ const char *pz_file_path;
+ const char *pz_file;
{
char z[ MAXPATHLEN ];
char* pz;
for interpretation by the invoking shell */
-void
+static void extract_quoted_files PARAMS ((char *, const char *, regmatch_t *));
+static void
extract_quoted_files (pz_data, pz_fixed_file, p_re_match)
char *pz_data;
const char *pz_fixed_file;
char *pz_dir_end = strrchr (pz_fixed_file, '/');
char *pz_incl_quot = pz_data;
- fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
+ if (VLEVEL( VERB_APPLIES ))
+ fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
/* Set "pz_fixed_file" to point to the containing subdirectory of the source
If there is none, then it is in our current directory, ".". */
pz_incl_quot += p_re_match->rm_so;
/* Skip forward to the included file name */
- while (ISSPACE (*pz_incl_quot))
+ while (*pz_incl_quot != '"')
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_fixed_file, pz_incl_quot))
{
Somebody wrote a *_fix subroutine that we must call.
*/
-
-int
+#ifndef SEPARATE_FIX_PROC
+static int internal_fix PARAMS ((int, tFixDesc *));
+static int
internal_fix (read_fd, p_fixd)
int read_fd;
tFixDesc* p_fixd;
/*
* Parent in error
*/
- fprintf (stderr, z_fork_err, errno, strerror (errno),
+ fprintf (stderr, z_fork_err, errno, xstrerror (errno),
p_fixd->fix_name);
{
static int failCt = 0;
*/
fcntl (fd[1], F_DUPFD, STDOUT_FILENO);
fcntl (read_fd, F_DUPFD, STDIN_FILENO);
- fdopen (STDIN_FILENO, "r");
- fdopen (STDOUT_FILENO, "w");
- apply_fix (p_fixd->patch_args[0], pz_curr_file);
+ apply_fix (p_fixd, pz_curr_file);
exit (0);
}
+#endif /* !SEPARATE_FIX_PROC */
+#ifdef SEPARATE_FIX_PROC
+static void
+fix_with_system (p_fixd, pz_fix_file, pz_file_source, pz_temp_file)
+ tFixDesc* p_fixd;
+ tCC* pz_fix_file;
+ tCC* pz_file_source;
+ tCC* pz_temp_file;
+{
+ char* pz_cmd;
+ char* pz_scan;
+ size_t argsize;
+
+ if (p_fixd->fd_flags & FD_SUBROUTINE)
+ {
+ tSCC z_applyfix_prog[] = "/fixinc/applyfix";
+
+ argsize = 32
+ + strlen( pz_orig_dir )
+ + sizeof( z_applyfix_prog )
+ + strlen( pz_fix_file )
+ + strlen( pz_file_source )
+ + strlen( pz_temp_file );
+
+ pz_cmd = (char*)xmalloc( argsize );
+
+ strcpy( pz_cmd, pz_orig_dir );
+ pz_scan = pz_cmd + strlen( pz_orig_dir );
+ strcpy( pz_scan, z_applyfix_prog );
+ pz_scan += sizeof( z_applyfix_prog ) - 1;
+ *(pz_scan++) = ' ';
+
+ /*
+ * Now add the fix number and file names that may be needed
+ */
+ sprintf (pz_scan, "%ld %s %s %s", p_fixd - fixDescList,
+ pz_fix_file, pz_file_source, pz_temp_file);
+ }
+ else /* NOT an "internal" fix: */
+ {
+ size_t parg_size;
+#ifdef __MSDOS__
+ /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
+ dst is a temporary file anyway, so we know there's no other
+ file by that name; and DOS's system(3) doesn't mind to
+ clobber existing file in redirection. Besides, with DOS 8+3
+ limited file namespace, we can easily lose if dst already has
+ an extension that is 3 or more characters long.
+
+ I do not think the 8+3 issue is relevant because all the files
+ we operate on are named "*.h", making 8+2 adequate. Anyway,
+ the following bizarre use of 'cat' only works on DOS boxes.
+ It causes the file to be dropped into a temporary file for
+ 'cat' to read (pipes do not work on DOS). */
+ tSCC z_cmd_fmt[] = " %s | cat > %s";
+#else
+ /* Don't use positional formatting arguments because some lame-o
+ implementations cannot cope :-(. */
+ tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
+#endif
+ tCC** ppArgs = p_fixd->patch_args;
+
+ argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
+ + strlen( pz_file_source );
+ parg_size = argsize;
+
+
+ /*
+ * Compute the size of the command line. Add lotsa extra space
+ * because some of the args to sed use lotsa single quotes.
+ * (This requires three extra bytes per quote. Here we allow
+ * for up to 8 single quotes for each argument, including the
+ * command name "sed" itself. Nobody will *ever* need more. :)
+ */
+ for (;;)
+ {
+ tCC* p_arg = *(ppArgs++);
+ if (p_arg == NULL)
+ break;
+ argsize += 24 + strlen( p_arg );
+ }
+
+ /* Estimated buffer size we will need. */
+ pz_scan = pz_cmd = (char*)xmalloc( argsize );
+ /* How much of it do we allot to the program name and its
+ arguments. */
+ parg_size = argsize - parg_size;
+
+ ppArgs = p_fixd->patch_args;
+
+ /*
+ * Copy the program name, unquoted
+ */
+ {
+ tCC* pArg = *(ppArgs++);
+ for (;;)
+ {
+ char ch = *(pArg++);
+ if (ch == NUL)
+ break;
+ *(pz_scan++) = ch;
+ }
+ }
+
+ /*
+ * Copy the program arguments, quoted
+ */
+ for (;;)
+ {
+ tCC* pArg = *(ppArgs++);
+ char* pz_scan_save;
+ if (pArg == NULL)
+ break;
+ *(pz_scan++) = ' ';
+ pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
+ parg_size - (pz_scan - pz_cmd) );
+ /*
+ * Make sure we don't overflow the buffer due to sloppy
+ * size estimation.
+ */
+ while (pz_scan == (char*)NULL)
+ {
+ size_t already_filled = pz_scan_save - pz_cmd;
+ pz_cmd = (char*)xrealloc( pz_cmd, argsize += 100 );
+ pz_scan_save = pz_scan = pz_cmd + already_filled;
+ parg_size += 100;
+ pz_scan = make_raw_shell_str( pz_scan, pArg,
+ parg_size - (pz_scan - pz_cmd) );
+ }
+ }
+
+ /*
+ * add the file machinations.
+ */
+#ifdef __MSDOS__
+ sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
+#else
+ sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
+ pz_temp_file, pz_temp_file, pz_temp_file);
+#endif
+ }
+ system( pz_cmd );
+ free( (void*)pz_cmd );
+}
+
/* * * * * * * * * * * * *
This loop should only cycle for 1/2 of one loop.
its stdin and returns the new fd this process will use
for stdout. */
-int
+#else /* is *NOT* SEPARATE_FIX_PROC */
+static int start_fixer PARAMS ((int, tFixDesc *, char *));
+static int
start_fixer (read_fd, p_fixd, pz_fix_file)
int read_fd;
tFixDesc* p_fixd;
else
{
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);
- }
+ pz_cmd = (char*) xmalloc (strlen (p_fixd->patch_args[2])
+ + sizeof( z_cmd_fmt )
+ + strlen( pz_fix_file ));
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;
}
+ /* 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.
+
+ */
for (;;)
{
static int failCt = 0;
int fd;
fd = chain_open (read_fd,
- (t_pchar *) p_fixd->patch_args,
+ (tCC **) p_fixd->patch_args,
(process_chain_head == -1)
? &process_chain_head : (pid_t *) NULL);
break;
}
- fprintf (stderr, z_fork_err, errno, strerror (errno),
+ fprintf (stderr, z_fork_err, errno, xstrerror (errno),
p_fixd->fix_name);
if ((errno != EAGAIN) || (++failCt > 10))
sleep (1);
}
+ /* 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);
return read_fd;
}
+#endif
/* * * * * * * * * * * * *
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
+static t_bool fix_applies PARAMS ((tFixDesc *));
+static t_bool
fix_applies (p_fixd)
tFixDesc *p_fixd;
{
+ const char *pz_fname = pz_curr_file;
+ const char *pz_scan = p_fixd->file_list;
int test_ct;
tTestDesc *p_test;
+# ifdef SEPARATE_FIX_PROC
+ /*
+ * There is only one fix that uses a shell script as of this writing.
+ * I hope to nuke it anyway, it does not apply to DOS and it would
+ * be painful to implement. Therefore, no "shell" fixes for DOS.
+ */
+ if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
+ return BOOL_FALSE;
+# else
if (p_fixd->fd_flags & FD_SKIP_TEST)
return BOOL_FALSE;
+# endif
/* IF there is a file name restriction,
THEN ensure the current file name matches one in the pattern */
- if (p_fixd->file_list != (char *) NULL)
+ if (pz_scan != (char *) NULL)
{
- const char *pz_fname = pz_curr_file;
- const char *pz_scan = p_fixd->file_list;
size_t name_len;
while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
switch (p_test->type)
{
case TT_TEST:
- if (test_test (p_test, pz_curr_file) != APPLY_FIX)
+ 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
return BOOL_FALSE;
+ }
break;
case TT_EGREP:
- if (egrep_test (pz_curr_data, p_test) != APPLY_FIX)
+ 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
return BOOL_FALSE;
+ }
break;
case TT_NEGREP:
- if (egrep_test (pz_curr_data, p_test) == APPLY_FIX)
+ 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
/* Negated sense */
return BOOL_FALSE;
+ }
break;
case TT_FUNCTION:
if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
- != APPLY_FIX)
+ != 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
return BOOL_FALSE;
+ }
break;
}
}
Write out a replacement file */
-void
+static void write_replacement PARAMS ((tFixDesc *));
+static void
write_replacement (p_fixd)
tFixDesc *p_fixd;
{
return;
{
- FILE* out_fp = create_file (pz_curr_file);
+ FILE* out_fp = create_file ();
fputs (pz_text, out_fp);
fclose (out_fp);
}
the matched text and then copy any remaining data from the
output of the filter chain.
*/
-void
+static void test_for_changes PARAMS ((int));
+static void
test_for_changes (read_fd)
int read_fd;
{
*/
else if (ch != *pz_cmp)
{
- out_fp = create_file (pz_curr_file);
+ out_fp = create_file ();
#ifdef DO_STATS
altered_ct++;
void
process ()
{
- static char env_current_file[1024];
tFixDesc *p_fixd = fixDescList;
int todo_ct = FIX_COUNT;
int read_fd = -1;
+# ifndef SEPARATE_FIX_PROC
int num_children = 0;
+# else /* is SEPARATE_FIX_PROC */
+ char* pz_file_source = pz_curr_file;
+# endif
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, strerror (erno));
+ erno, xstrerror (erno));
return;
}
#ifdef DO_STATS
process_ct++;
#endif
- fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
- if (strstr (pz_curr_data, gnu_lib_mark) != (char *) NULL)
- {
- UNLOAD_DATA();
- return;
- }
+ if (VLEVEL( VERB_PROGRESS ) && have_tty)
+ fprintf (stderr, "%6d %-50s \r", data_map_size, pz_curr_file );
+# ifndef SEPARATE_FIX_PROC
process_chain_head = NOPROCESS;
/* For every fix in our fix list, ... */
if (! fix_applies (p_fixd))
continue;
- fprintf (stderr, "Applying %-24s to %s\n",
- p_fixd->fix_name, pz_curr_file);
+ if (VLEVEL( VERB_APPLIES ))
+ fprintf (stderr, "Applying %-24s to %s\n",
+ p_fixd->fix_name, pz_curr_file);
if (p_fixd->fd_flags & FD_REPLACEMENT)
{
if (read_fd < 0)
{
fprintf (stderr, "Error %d (%s) opening %s\n", errno,
- strerror (errno), pz_curr_file);
+ xstrerror (errno), pz_curr_file);
exit (EXIT_FAILURE);
}
} while (--num_children > 0);
}
+# else /* is SEPARATE_FIX_PROC */
+
+ for (; todo_ct > 0; p_fixd++, todo_ct--)
+ {
+ if (! fix_applies (p_fixd))
+ continue;
+
+ if (VLEVEL( VERB_APPLIES ))
+ fprintf (stderr, "Applying %-24s to %s\n",
+ p_fixd->fix_name, pz_curr_file);
+
+ if (p_fixd->fd_flags & FD_REPLACEMENT)
+ {
+ write_replacement (p_fixd);
+ UNLOAD_DATA();
+ return;
+ }
+ fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
+ pz_file_source = pz_temp_file;
+ }
+
+ read_fd = open (pz_temp_file, O_RDONLY);
+ if (read_fd < 0)
+ {
+ if (errno != ENOENT)
+ fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
+ errno, xstrerror (errno), pz_temp_file);
+ }
+ else
+ {
+ test_for_changes (read_fd);
+ /* Unlinking a file while it is still open is a Bad Idea on
+ DOS/Windows. */
+ close (read_fd);
+ unlink (pz_temp_file);
+ }
+
+# endif
UNLOAD_DATA();
}