mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-28 06:14:10 +08:00
2f82a97b02
Co-Authored-By: Daniel Franke <franke.daniel@gmail.com> From-SVN: r120529
1362 lines
35 KiB
C
1362 lines
35 KiB
C
/* 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 GCC will search.
|
||
|
||
Copyright (C) 1997, 1998, 1999, 2000, 2004 Free Software Foundation, Inc.
|
||
|
||
This file is part of GCC.
|
||
|
||
GCC is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
GCC is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GCC; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||
Boston, MA 02110-1301, USA. */
|
||
|
||
#include "fixlib.h"
|
||
|
||
#include <fnmatch.h>
|
||
#include <sys/stat.h>
|
||
#ifndef SEPARATE_FIX_PROC
|
||
#include <sys/wait.h>
|
||
#endif
|
||
|
||
#if defined( HAVE_MMAP_FILE )
|
||
#include <sys/mman.h>
|
||
#define BAD_ADDR ((void*)-1)
|
||
#endif
|
||
|
||
#ifndef SEPARATE_FIX_PROC
|
||
#include "server.h"
|
||
#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";
|
||
|
||
/* This format will be used at the start of every generated file */
|
||
|
||
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";
|
||
|
||
int find_base_len = 0;
|
||
|
||
typedef enum {
|
||
VERB_SILENT = 0,
|
||
VERB_FIXES,
|
||
VERB_APPLIES,
|
||
VERB_PROGRESS,
|
||
VERB_TESTS,
|
||
VERB_EVERYTHING
|
||
} te_verbose;
|
||
|
||
te_verbose verbose_level = VERB_PROGRESS;
|
||
int have_tty = 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 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;
|
||
|
||
static void do_version (void) ATTRIBUTE_NORETURN;
|
||
char *load_file (const char *);
|
||
void run_compiles (void);
|
||
void initialize (int argc, char** argv);
|
||
void process (void);
|
||
|
||
/* External Source Code */
|
||
|
||
#include "fixincl.x"
|
||
|
||
/* * * * * * * * * * * * * * * * * * *
|
||
*
|
||
* MAIN ROUTINE
|
||
*/
|
||
extern int main (int, char **);
|
||
int
|
||
main (int argc, char** argv)
|
||
{
|
||
char *file_name_buf;
|
||
|
||
initialize ( argc, argv );
|
||
|
||
have_tty = isatty (fileno (stderr));
|
||
|
||
/* Before anything else, ensure we can allocate our file name buffer. */
|
||
file_name_buf = load_file_data (stdin);
|
||
|
||
/* 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 */
|
||
|
||
freopen ("/dev/null", "r", stdin);
|
||
|
||
if (file_name_buf == (char *) NULL)
|
||
{
|
||
fputs ("No file names listed for fixing\n", stderr);
|
||
exit (EXIT_FAILURE);
|
||
}
|
||
|
||
for (;;)
|
||
{
|
||
char* pz_end;
|
||
|
||
/* skip to start of name, past any "./" prefixes */
|
||
|
||
while (ISSPACE (*file_name_buf)) file_name_buf++;
|
||
while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
|
||
file_name_buf += 2;
|
||
|
||
/* Check for end of list */
|
||
|
||
if (*file_name_buf == NUL)
|
||
break;
|
||
|
||
/* Set global file name pointer and find end of name */
|
||
|
||
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;
|
||
|
||
while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
|
||
|
||
/* IF no name is found (blank line) or comment marker, skip line */
|
||
|
||
if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
|
||
continue;
|
||
*pz_end = NUL;
|
||
|
||
process ();
|
||
} /* for (;;) */
|
||
|
||
#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 */
|
||
|
||
# ifdef SEPARATE_FIX_PROC
|
||
unlink( pz_temp_file );
|
||
# endif
|
||
exit (EXIT_SUCCESS);
|
||
}
|
||
|
||
|
||
static void
|
||
do_version (void)
|
||
{
|
||
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);
|
||
#ifndef SEPARATE_FIX_PROC
|
||
puts (zBuf + 5);
|
||
exit (strcmp (run_shell (zBuf), program_id));
|
||
#else
|
||
exit (system (zBuf));
|
||
#endif
|
||
}
|
||
|
||
/* * * * * * * * * * * * */
|
||
|
||
void
|
||
initialize ( int argc, char** argv )
|
||
{
|
||
xmalloc_set_program_name (argv[0]);
|
||
|
||
switch (argc)
|
||
{
|
||
case 1:
|
||
break;
|
||
|
||
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;
|
||
|
||
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
|
||
|
||
initialize_opts ();
|
||
|
||
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;
|
||
|
||
default:
|
||
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;
|
||
}
|
||
if (verbose_level >= VERB_EVERYTHING) {
|
||
verbose_level = VERB_EVERYTHING;
|
||
fputs ("fixinc verbosity: EVERYTHING\n", stderr);
|
||
}
|
||
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);
|
||
signal (SIGIOT, SIG_IGN);
|
||
signal (SIGPIPE, SIG_IGN);
|
||
signal (SIGALRM, SIG_IGN);
|
||
signal (SIGTERM, SIG_IGN);
|
||
}
|
||
|
||
/* * * * * * * * * * * * *
|
||
|
||
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 ( const char* fname )
|
||
{
|
||
struct stat stbf;
|
||
char* res;
|
||
|
||
if (stat (fname, &stbf) != 0)
|
||
{
|
||
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)
|
||
{
|
||
if (NOT_SILENT)
|
||
fprintf (stderr, "error %d (%s) opening %s for read\n",
|
||
errno, xstrerror (errno), fname);
|
||
return (char*)NULL;
|
||
}
|
||
|
||
#ifdef HAVE_MMAP_FILE
|
||
curr_data_mapped = BOOL_TRUE;
|
||
|
||
/* 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 (fp);
|
||
fclose (fp);
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
static int
|
||
machine_matches( tFixDesc* p_fixd )
|
||
{
|
||
char const ** papz_machs = p_fixd->papz_machs;
|
||
int have_match = BOOL_FALSE;
|
||
|
||
for (;;)
|
||
{
|
||
char const * pz_mpat = *(papz_machs++);
|
||
if (pz_mpat == NULL)
|
||
break;
|
||
if (fnmatch(pz_mpat, pz_machine, 0) == 0)
|
||
{
|
||
have_match = BOOL_TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Check for sense inversion then set the "skip test" flag, if needed */
|
||
if (p_fixd->fd_flags & FD_MACH_IFNOT)
|
||
have_match = ! have_match;
|
||
|
||
if (! have_match)
|
||
p_fixd->fd_flags |= FD_SKIP_TEST;
|
||
|
||
return have_match;
|
||
}
|
||
|
||
/* * * * * * * * * * * * *
|
||
*
|
||
* run_compiles run all the regexp compiles for all the fixes once.
|
||
*/
|
||
void
|
||
run_compiles (void)
|
||
{
|
||
tFixDesc *p_fixd = fixDescList;
|
||
int fix_ct = FIX_COUNT;
|
||
regex_t *p_re = XCNEWVEC (regex_t, REGEX_COUNT);
|
||
|
||
/* Make sure compile_re does not stumble across invalid data */
|
||
|
||
memset (&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, ... */
|
||
|
||
while (--test_ct >= 0)
|
||
{
|
||
switch (p_test->type)
|
||
{
|
||
case TT_EGREP:
|
||
case TT_NEGREP:
|
||
p_test->p_test_regex = p_re++;
|
||
compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
|
||
"select test", p_fixd->fix_name);
|
||
default: break;
|
||
}
|
||
p_test++;
|
||
}
|
||
}
|
||
while (p_fixd++, --fix_ct > 0);
|
||
}
|
||
|
||
|
||
/* * * * * * * * * * * * *
|
||
|
||
create_file Create the output modified file.
|
||
Input: the name of the file to create
|
||
Returns: a file pointer to the new, open file */
|
||
|
||
#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
|
||
|
||
|
||
static FILE *
|
||
create_file (void)
|
||
{
|
||
int fd;
|
||
FILE *pf;
|
||
char fname[MAXPATHLEN];
|
||
|
||
sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
|
||
|
||
fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
|
||
|
||
/* We may need to create the directories needed... */
|
||
if ((fd < 0) && (errno == ENOENT))
|
||
{
|
||
char *pz_dir = strchr (fname + 1, '/');
|
||
struct stat stbf;
|
||
|
||
while (pz_dir != (char *) NULL)
|
||
{
|
||
*pz_dir = NUL;
|
||
if (stat (fname, &stbf) < 0)
|
||
{
|
||
#ifdef _WIN32
|
||
mkdir (fname);
|
||
#else
|
||
mkdir (fname, S_IFDIR | S_DIRALL);
|
||
#endif
|
||
}
|
||
|
||
*pz_dir = '/';
|
||
pz_dir = strchr (pz_dir + 1, '/');
|
||
}
|
||
|
||
/* Now, lets try the open again... */
|
||
fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
|
||
}
|
||
if (fd < 0)
|
||
{
|
||
fprintf (stderr, "Error %d (%s) creating %s\n",
|
||
errno, xstrerror (errno), fname);
|
||
exit (EXIT_FAILURE);
|
||
}
|
||
if (NOT_SILENT)
|
||
fprintf (stderr, "Fixed: %s\n", pz_curr_file);
|
||
pf = fdopen (fd, "w");
|
||
|
||
/*
|
||
* 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);
|
||
|
||
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: APPLY_FIX or SKIP_FIX, depending on the result of the
|
||
shell script we run. */
|
||
#ifndef SEPARATE_FIX_PROC
|
||
static int
|
||
test_test (tTestDesc* p_test, char* pz_test_file)
|
||
{
|
||
tSCC cmd_fmt[] =
|
||
"file=%s\n\
|
||
if ( test %s ) > /dev/null 2>&1\n\
|
||
then echo TRUE\n\
|
||
else echo FALSE\n\
|
||
fi";
|
||
|
||
char *pz_res;
|
||
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);
|
||
|
||
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 );
|
||
res = SKIP_FIX;
|
||
}
|
||
|
||
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
|
||
|
||
/* * * * * * * * * * * * *
|
||
|
||
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: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
|
||
|
||
The caller may choose to reverse meaning if the sense of the test
|
||
is inverted. */
|
||
|
||
static int
|
||
egrep_test (char* pz_data, tTestDesc* p_test)
|
||
{
|
||
#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 (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
|
||
return APPLY_FIX;
|
||
return SKIP_FIX;
|
||
}
|
||
|
||
|
||
/* * * * * * * * * * * * *
|
||
|
||
quoted_file_exists Make sure that a file exists before we emit
|
||
the file name. If we emit the name, our invoking shell will try
|
||
to copy a non-existing file into the destination directory. */
|
||
|
||
static int
|
||
quoted_file_exists (const char* pz_src_path,
|
||
const char* pz_file_path,
|
||
const char* pz_file)
|
||
{
|
||
char z[ MAXPATHLEN ];
|
||
char* pz;
|
||
sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
|
||
pz = z + strlen ( z );
|
||
|
||
for (;;) {
|
||
char ch = *pz_file++;
|
||
if (! ISGRAPH( ch ))
|
||
return 0;
|
||
if (ch == '"')
|
||
break;
|
||
*pz++ = ch;
|
||
}
|
||
*pz = '\0';
|
||
{
|
||
struct stat s;
|
||
if (stat (z, &s) != 0)
|
||
return 0;
|
||
return S_ISREG( s.st_mode );
|
||
}
|
||
}
|
||
|
||
|
||
/* * * * * * * * * * * * *
|
||
*
|
||
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
|
||
another directory, any files that are included by that modified
|
||
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
|
||
|
||
Input: the text of the file, the file name and a pointer to the
|
||
match list where the match information was stored.
|
||
Result: internally nothing. The results are written to stdout
|
||
for interpretation by the invoking shell */
|
||
|
||
|
||
static void
|
||
extract_quoted_files (char* pz_data,
|
||
const char* pz_fixed_file,
|
||
regmatch_t* p_re_match)
|
||
{
|
||
char *pz_dir_end = strrchr (pz_fixed_file, '/');
|
||
char *pz_incl_quot = pz_data;
|
||
|
||
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, ".". */
|
||
|
||
if (pz_dir_end == (char *) NULL)
|
||
pz_fixed_file = ".";
|
||
else
|
||
*pz_dir_end = '\0';
|
||
|
||
for (;;)
|
||
{
|
||
pz_incl_quot += p_re_match->rm_so;
|
||
|
||
/* Skip forward to the included file name */
|
||
while (*pz_incl_quot != '"')
|
||
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_fixed_file);
|
||
pz_dir_end = pz_incl_quot;
|
||
|
||
/* Append to the directory the relative path of the desired file */
|
||
while (*pz_incl_quot != '"')
|
||
putc (*pz_incl_quot++, stdout);
|
||
|
||
/* Now print the destination directory appended with the
|
||
relative path of the desired file */
|
||
printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
|
||
while (*pz_dir_end != '"')
|
||
putc (*pz_dir_end++, stdout);
|
||
|
||
/* End of entry */
|
||
putc ('\n', stdout);
|
||
}
|
||
|
||
/* Find the next entry */
|
||
if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* * * * * * * * * * * * *
|
||
|
||
Somebody wrote a *_fix subroutine that we must call.
|
||
*/
|
||
#ifndef SEPARATE_FIX_PROC
|
||
static int
|
||
internal_fix (int read_fd, tFixDesc* p_fixd)
|
||
{
|
||
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];
|
||
}
|
||
|
||
/*
|
||
* 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:;
|
||
|
||
/*
|
||
* Close our current stdin and stdout
|
||
*/
|
||
close (STDIN_FILENO);
|
||
close (STDOUT_FILENO);
|
||
UNLOAD_DATA();
|
||
|
||
/*
|
||
* Make the fd passed in the stdin, and the write end of
|
||
* the new pipe become the stdout.
|
||
*/
|
||
dup2 (fd[1], STDOUT_FILENO);
|
||
dup2 (read_fd, STDIN_FILENO);
|
||
|
||
apply_fix (p_fixd, pz_curr_file);
|
||
exit (0);
|
||
}
|
||
#endif /* !SEPARATE_FIX_PROC */
|
||
|
||
|
||
#ifdef SEPARATE_FIX_PROC
|
||
static void
|
||
fix_with_system (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)
|
||
{
|
||
static const char z_applyfix_prog[] =
|
||
"/../fixincludes/applyfix" EXE_EXT;
|
||
|
||
struct stat buf;
|
||
argsize = 32
|
||
+ strlen (pz_orig_dir)
|
||
+ sizeof (z_applyfix_prog)
|
||
+ strlen (pz_fix_file)
|
||
+ strlen (pz_file_source)
|
||
+ strlen (pz_temp_file);
|
||
|
||
/* Allocate something sure to be big enough for our purposes */
|
||
pz_cmd = XNEWVEC (char, argsize);
|
||
strcpy (pz_cmd, pz_orig_dir);
|
||
pz_scan = pz_cmd + strlen (pz_orig_dir);
|
||
|
||
strcpy (pz_scan, z_applyfix_prog);
|
||
|
||
/* IF we can't find the "applyfix" executable file at the first guess,
|
||
try one level higher up */
|
||
if (stat (pz_cmd, &buf) == -1)
|
||
{
|
||
strcpy (pz_scan, "/..");
|
||
strcpy (pz_scan+3, z_applyfix_prog);
|
||
}
|
||
|
||
pz_scan += strlen (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 = XNEWVEC (char, 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 = 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.
|
||
"chain_open" starts a process that uses "read_fd" as
|
||
its stdin and returns the new fd this process will use
|
||
for stdout. */
|
||
|
||
#else /* is *NOT* SEPARATE_FIX_PROC */
|
||
static int
|
||
start_fixer (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 = NULL;
|
||
pz_cmd_save = NULL;
|
||
}
|
||
else
|
||
{
|
||
tSCC z_cmd_fmt[] = "file='%s'\n%s";
|
||
pz_cmd = XNEWVEC (char, 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,
|
||
(tCC **) p_fixd->patch_args,
|
||
(process_chain_head == -1)
|
||
? &process_chain_head : (pid_t *) NULL);
|
||
|
||
if (fd != -1)
|
||
{
|
||
read_fd = fd;
|
||
break;
|
||
}
|
||
|
||
fprintf (stderr, z_fork_err, errno, xstrerror (errno),
|
||
p_fixd->fix_name);
|
||
|
||
if ((errno != EAGAIN) || (++failCt > 10))
|
||
exit (EXIT_FAILURE);
|
||
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);
|
||
p_fixd->patch_args[2] = pz_cmd_save;
|
||
}
|
||
|
||
return read_fd;
|
||
}
|
||
#endif
|
||
|
||
|
||
/* * * * * * * * * * * * *
|
||
*
|
||
* 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.
|
||
*/
|
||
static t_bool
|
||
fix_applies (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 (pz_scan != (char *) NULL)
|
||
{
|
||
size_t name_len;
|
||
|
||
while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
|
||
pz_fname += 2;
|
||
name_len = strlen (pz_fname);
|
||
|
||
for (;;)
|
||
{
|
||
if (fnmatch (pz_scan, pz_fname, 0) == 0)
|
||
break;
|
||
pz_scan += strlen (pz_scan) + 1;
|
||
if (*pz_scan == NUL)
|
||
return BOOL_FALSE;
|
||
}
|
||
}
|
||
|
||
/* 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++)
|
||
{
|
||
switch (p_test->type)
|
||
{
|
||
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
|
||
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
|
||
return BOOL_FALSE;
|
||
}
|
||
break;
|
||
|
||
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
|
||
/* 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) {
|
||
#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;
|
||
}
|
||
}
|
||
|
||
return BOOL_TRUE;
|
||
}
|
||
|
||
|
||
/* * * * * * * * * * * * *
|
||
|
||
Write out a replacement file */
|
||
|
||
static void
|
||
write_replacement (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 ();
|
||
size_t sz = strlen (pz_text);
|
||
fwrite (pz_text, sz, 1, out_fp);
|
||
if (pz_text[ sz-1 ] != '\n')
|
||
fputc ('\n', 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.
|
||
*/
|
||
static void
|
||
test_for_changes (int read_fd)
|
||
{
|
||
FILE *in_fp = fdopen (read_fd, "r");
|
||
FILE *out_fp = (FILE *) NULL;
|
||
unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
|
||
|
||
#ifdef DO_STATS
|
||
fixed_ct++;
|
||
#endif
|
||
for (;;)
|
||
{
|
||
int ch;
|
||
|
||
ch = getc (in_fp);
|
||
if (ch == EOF)
|
||
break;
|
||
ch &= 0xFF; /* all bytes are 8 bits */
|
||
|
||
/* 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)
|
||
{
|
||
out_fp = create_file ();
|
||
|
||
#ifdef DO_STATS
|
||
altered_ct++;
|
||
#endif
|
||
/* IF there are matched data, write the matched part now. */
|
||
if ((char*)pz_cmp != pz_curr_data)
|
||
fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
|
||
1, out_fp);
|
||
|
||
/* 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;
|
||
|
||
/* Close the file and see if we have to worry about
|
||
`#include "file.h"' constructs. */
|
||
fclose (out_fp);
|
||
if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
|
||
extract_quoted_files (pz_curr_data, pz_curr_file, &match);
|
||
}
|
||
|
||
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 (void)
|
||
{
|
||
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, xstrerror (erno));
|
||
return;
|
||
}
|
||
|
||
pz_curr_data = load_file (pz_curr_file);
|
||
if (pz_curr_data == (char *) NULL)
|
||
return;
|
||
|
||
#ifdef DO_STATS
|
||
process_ct++;
|
||
#endif
|
||
if (VLEVEL( VERB_PROGRESS ) && have_tty)
|
||
fprintf (stderr, "%6lu %-50s \r",
|
||
(unsigned long) data_map_size, pz_curr_file);
|
||
|
||
# ifndef SEPARATE_FIX_PROC
|
||
process_chain_head = NOPROCESS;
|
||
|
||
/* For every fix in our fix list, ... */
|
||
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;
|
||
}
|
||
|
||
/* 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. */
|
||
|
||
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);
|
||
}
|
||
|
||
/* Ensure we do not get duplicate output */
|
||
|
||
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);
|
||
}
|
||
|
||
# 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();
|
||
}
|