blob: 91ed0c88b4835e05f5bf29f7330b6007aad14d9f [file] [log] [blame]
/*
* $Id: fixincl.c,v 1.4 1998/08/05 10:20:11 korbb Exp $
*
* 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.
*
* See README-fixinc for more information.
*
* fixincl is free software.
*
* You may 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.
*
* fixincl 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 fixincl. See the file "COPYING". If not,
* write to: The Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include "regex.h"
#include "server.h"
#define tSCC static const char
#define tCC const char
#define tSC static char
typedef int tSuccess;
#define FAILURE ((tSuccess)-1)
#define SUCCESS ((tSuccess) 0)
#define PROBLEM ((tSuccess) 1)
#define SUCCEEDED( p ) ((p) == SUCCESS)
#define SUCCESSFUL( p ) SUCCEEDED( p )
#define FAILED( p ) ((p) < SUCCESS)
#define HADGLITCH( p ) ((p) > SUCCESS)
#define NUL '\0'
typedef enum
{
TT_TEST, TT_EGREP, TT_NEGREP
}
teTestType;
typedef struct test_desc tTestDesc;
struct test_desc
{
teTestType type;
const char *pzTest;
regex_t *pTestRegex;
};
typedef enum
{
PATCH_SED, PATCH_SHELL
}
tePatch;
typedef struct patch_desc tPatchDesc;
typedef struct fix_desc tFixDesc;
struct fix_desc
{
const char *pzFixName;
const char *pzFileList;
regex_t *pListRegex;
int testCt;
tTestDesc *pTestDesc;
const char **papzPatchArgs;
};
char *pzDestDir = (char *) NULL;
char *pzSrcDir = (char *) NULL;
pid_t chainHead = (pid_t) - 1;
const char zInclQuote[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
regex_t inclQuoteRegex;
char zFileNameBuf[0x8000];
char *loadFile (const char *pzFile);
void process (char *data, const char *dir, const char *file);
void runCompiles (void);
#include "fixincl.x"
int
main (argc, argv)
int argc;
char **argv;
{
static const char zGnuLib[] =
"This file is part of the GNU C Library";
#ifndef NO_BOGOSITY_LIMITS
size_t loopCt;
#endif
char *apzNames[128];
size_t fileNameCt;
if (argc != 1)
{
if (argc != 2)
{
fputs ("fixincl ERROR: files specified on command line (not stdin)\n",
stderr);
exit (EXIT_FAILURE);
}
if (strcmp (argv[1], "-v") == 0)
{
fputs ("$Id: fixincl.c,v 1.3 1998/06/02 07:00:12 korbb Exp $\n", stderr);
exit (EXIT_SUCCESS);
}
freopen (argv[1], "r", stdin);
}
pzDestDir = getenv ("DESTDIR");
if (pzDestDir == (char *) NULL)
{
fprintf (stderr, "fixincl ERROR: %s cannot find destination dir\n"
"\t(`DESTDIR' must be an environment variable)\n", *argv);
exit (EXIT_FAILURE);
}
pzSrcDir = getenv ("SRCDIR");
if (pzSrcDir == (char *) NULL)
{
fprintf (stderr, "fixincl ERROR: %s cannot find source dir\n"
"\t(`SRCDIR' must be an environment variable)\n", *argv);
exit (EXIT_FAILURE);
}
runCompiles ();
#ifndef NO_BOGOSITY_LIMITS
for (;;)
{
char *pzBuf;
pid_t child;
/*
* 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.)
*/
for (fileNameCt = 0, pzBuf = zFileNameBuf;
(fileNameCt < 128)
&& (pzBuf
< (zFileNameBuf + sizeof (zFileNameBuf) - MAXPATHLEN));
)
{
if (fgets (pzBuf, MAXPATHLEN, stdin) == (char *) NULL)
break;
while (isspace (*pzBuf))
pzBuf++;
apzNames[fileNameCt++] = pzBuf;
pzBuf += strlen (pzBuf);
while (isspace (pzBuf[-1]))
pzBuf--;
*pzBuf++ = '\0';
}
if (fileNameCt == 0)
return EXIT_SUCCESS;
child = fork ();
if (child == NULLPROCESS)
break;
if (child == NOPROCESS)
{
fprintf (stderr, "Error %d (%s) forking in main\n",
errno, strerror (errno));
exit (EXIT_FAILURE);
}
waitpid (child, (int *) NULL, 0);
}
#else
#error "NON-BOGUS LIMITS NOT SUPPORTED?!?!"
#endif
/*
* For every file specified in stdandard in
* (except as throttled for bogus reasons)...
*/
for (loopCt = 0; loopCt < fileNameCt; loopCt++)
{
char *pzData;
char *pzFile = apzNames[loopCt];
if (access (pzFile, R_OK) != 0)
{
int erno = errno;
fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
pzFile, getcwd ((char *) NULL, MAXPATHLEN),
erno, strerror (erno));
}
else if (pzData = loadFile (pzFile),
(pzData != (char *) NULL))
{
if (strstr (pzData, zGnuLib) == (char *) NULL)
process (pzData, pzDestDir, pzFile);
free ((void *) pzData);
}
}
return EXIT_SUCCESS;
}
char *
loadFile (pzFile)
const char *pzFile;
{
char *pzDta;
size_t fileSize;
{
struct stat stbf;
if (stat (pzFile, &stbf) != 0)
{
fprintf (stderr, "error %d (%s) stat-ing %s\n",
errno, strerror (errno), pzFile);
return (char *) NULL;
}
fileSize = stbf.st_size;
}
if (fileSize == 0)
return (char *) NULL;
pzDta = (char *) malloc ((fileSize + 16) & ~0x00F);
if (pzDta == (char *) NULL)
{
fprintf (stderr, "error: could not malloc %d bytes\n",
fileSize);
exit (EXIT_FAILURE);
}
{
FILE *fp = fopen (pzFile, "r");
size_t sizeLeft = fileSize;
char *readPtr = pzDta;
if (fp == (FILE *) NULL)
{
fprintf (stderr, "error %d (%s) opening %s\n", errno,
strerror (errno), pzFile);
free ((void *) pzDta);
return (char *) NULL;
}
do
{
size_t sizeRead = fread ((void *) readPtr, 1, sizeLeft, fp);
if (sizeRead == 0)
{
if (feof (fp))
break;
if (ferror (fp))
{
fprintf (stderr, "error %d (%s) reading %s\n", errno,
strerror (errno), pzFile);
free ((void *) pzDta);
fclose (fp);
return (char *) NULL;
}
}
readPtr += sizeRead;
sizeLeft -= sizeRead;
}
while (sizeLeft != 0);
*readPtr = '\0';
fclose (fp);
return pzDta;
}
}
void
runCompiles ()
{
tSCC zBadComp[] = "fixincl ERROR: cannot compile %s regex for %s\n"
"\texpr = `%s'\n" "\terror %s\n";
tFixDesc *pFD = fixDescList;
int fixCt = FIX_COUNT;
tTestDesc *pTD;
int tstCt;
int reCt = REGEX_COUNT;
const char *pzErr;
regex_t *pRegex = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t));
if (pRegex == (regex_t *) NULL)
{
fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n",
REGEX_COUNT * sizeof (regex_t));
exit (EXIT_FAILURE);
}
re_set_syntax (RE_SYNTAX_EGREP);
pzErr = re_compile_pattern (zInclQuote, strlen (zInclQuote),
&inclQuoteRegex);
if (pzErr != (char *) NULL)
{
fprintf (stderr, zBadComp, "quoted include", "runCompiles",
zInclQuote, pzErr);
exit (EXIT_FAILURE);
}
/*
* FOR every fixup, ...
*/
for (;;)
{
pTD = pFD->pTestDesc;
tstCt = pFD->testCt;
/*
* FOR every test for the fixup, ...
*/
while (--tstCt >= 0)
{
switch (pTD->type)
{
case TT_EGREP:
case TT_NEGREP:
if (--reCt < 0)
{
fputs ("out of RE's\n", stderr);
exit (EXIT_FAILURE);
}
pTD->pTestRegex = pRegex++;
pzErr = re_compile_pattern (pTD->pzTest,
strlen (pTD->pzTest),
pTD->pTestRegex);
if (pzErr != (char *) NULL)
{
fprintf (stderr, zBadComp, "select test", pFD->pzFixName,
pTD->pzTest, pzErr);
exit (EXIT_FAILURE);
}
}
pTD++;
}
if (--fixCt <= 0)
break;
pFD++;
}
}
FILE *
createFile (pzFile)
const char *pzFile;
{
int fd;
FILE *pf;
char fname[MAXPATHLEN];
sprintf (fname, "%s/%s", pzDestDir, pzFile);
unlink (fname);
fd = open (fname, O_WRONLY | O_CREAT);
if ((fd < 0) && (errno == ENOENT))
{
char *pzDir = strchr (fname + 1, '/');
struct stat stbf;
while (pzDir != (char *) NULL)
{
*pzDir = NUL;
if (stat (fname, &stbf) < 0)
{
mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP
| S_IROTH | S_IXOTH);
}
*pzDir = '/';
pzDir = strchr (pzDir + 1, '/');
}
fd = open (fname, O_WRONLY | O_CREAT);
}
if (fd < 0)
{
fprintf (stderr, "Error %d (%s) creating %s\n",
errno, strerror (errno), fname);
exit (EXIT_FAILURE);
}
fprintf (stderr, "Fixed: %s\n", pzFile);
pf = fdopen (fd, "w");
#ifdef LATER
{
static const char zHdr[] =
"/*\n"
" * 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\n";
fprintf (pf, zHdr, pzFile);
}
#endif
return pf;
}
tSuccess
testTest (pTest, pzFile)
tTestDesc *pTest;
char* pzFile;
{
char *pzRes;
tSuccess res = FAILURE;
static char zCmdBuf[4096];
tSCC zCmdFmt[] = "file=%s\nif ( test %s ) > /dev/null 2>&1\n"
"then echo TRUE\n" "else echo FALSE\n" "fi";
sprintf (zCmdBuf, zCmdFmt, pzFile, pTest->pzTest);
pzRes = runShell (zCmdBuf);
if (*pzRes == 'T')
res = SUCCESS;
free ((void *) pzRes);
return res;
}
tSuccess
egrepTest (pzDta, pTest)
char *pzDta;
tTestDesc *pTest;
{
regmatch_t match;
#ifndef NO_BOGOSITY
if (pTest->pTestRegex == 0)
fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n", pTest->pzTest);
#endif
if (regexec (pTest->pTestRegex, pzDta, 1, &match, 0) == 0)
return SUCCESS;
return FAILURE;
}
void
extractQuotedFiles (pzDta, pzFile, pMatch)
char *pzDta;
const char *pzFile;
regmatch_t *pMatch;
{
char *pzDirEnd = strrchr (pzFile, '/');
char *pzInclQuot = pzDta;
fprintf (stderr, "Quoted includes in %s\n", pzFile);
/*
* Set "pzFile" to point to the containing subdirectory of the source
* If there is none, then it is in our current direcory, ".".
*/
if (pzDirEnd == (char *) NULL)
pzFile = ".";
else
*pzDirEnd = '\0';
for (;;)
{
pzInclQuot += pMatch->rm_so;
/*
* Skip forward to the included file name
*/
while (isspace (*pzInclQuot))
pzInclQuot++;
while (isspace (*++pzInclQuot));
pzInclQuot += sizeof ("include") - 1;
while (*pzInclQuot++ != '"');
/*
* Print the source directory and the subdirectory of the file
* in question.
*/
printf ("%s %s/", pzSrcDir, pzFile);
pzDirEnd = pzInclQuot;
/*
* Append to the directory the relative path of the desired file
*/
while (*pzInclQuot != '"')
putc (*pzInclQuot++, stdout);
/*
* Now print the destination directory appended with the relative
* path of the desired file
*/
printf (" %s/%s/", pzDestDir, pzFile);
while (*pzDirEnd != '"')
putc (*pzDirEnd++, stdout);
/*
* End of entry
*/
putc ('\n', stdout);
/*
* Find the next entry
*/
if (regexec (&inclQuoteRegex, pzInclQuot, 1, pMatch, 0) != 0)
break;
}
}
/*
* Process the potential fixes for a particular include file
*/
void
process (pzDta, pzDir, pzFile)
char *pzDta;
const char *pzDir;
const char *pzFile;
{
static char zEnvFile[1024] =
{"file="};
tFixDesc *pFD = fixDescList;
int todoCt = FIX_COUNT;
tFdPair fdp =
{-1, -1};
/*
* 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.
*/
if (zEnvFile[5] == NUL)
putenv (zEnvFile);
/*
* 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().
*/
strcpy (zEnvFile + 5, pzFile);
chainHead = NOPROCESS;
/*
* For every fix in our fix list, ...
*/
for (; todoCt > 0; pFD++, todoCt--)
{
tTestDesc *pTD;
int tstCt;
tSuccess egrepRes;
/*
* IF there is a file name restriction,
* THEN ensure the current file name matches one in the pattern
*/
if (pFD->pzFileList != (char *) NULL)
{
const char *pzFil = pzFile;
const char *pzScn = pFD->pzFileList;
size_t nmLen;
while ((pzFil[0] == '.') && (pzFil[1] == '/'))
pzFil += 2;
nmLen = strlen (pzFil);
for (;;)
{
pzScn = strstr (pzScn + 1, pzFil);
if (pzScn == (char *) NULL)
goto nextFix;
if ((pzScn[-1] == '|') && (pzScn[nmLen] == '|'))
break;
}
}
egrepRes = PROBLEM;
/*
* IF there are no tests
* THEN we always run the fixup
*/
for (pTD = pFD->pTestDesc, tstCt = pFD->testCt;
tstCt-- > 0;
pTD++)
{
switch (pTD->type)
{
case TT_TEST:
/*
* IF *any* of the shell tests fail,
* THEN do not process the fix.
*/
if (!SUCCESSFUL (testTest (pTD, pzFile)))
goto nextFix;
break;
case TT_EGREP:
/*
* IF we have not had a successful egrep test
* *AND* this test does not pass,
* THEN mark the egrep test as failing. It starts
* out as a "PROBLEM", meaning that if we do not
* encounter any egrep tests, then we will let it pass.
*/
if ((!SUCCESSFUL (egrepRes))
&& (!SUCCESSFUL (egrepTest (pzDta, pTD))))
egrepRes = FAILURE;
break;
case TT_NEGREP:
/*
* IF *any* of the negative egrep tests fail,
* THEN do not process the fix.
*/
if (SUCCESSFUL (egrepTest (pzDta, pTD)))
goto nextFix;
break;
}
}
/*
* IF there were no egrep tests *OR* at least one passed, ...
*/
if (!FAILED (egrepRes))
{
fprintf (stderr, "Applying %-32s to %s\n",
pFD->pzFixName, pzFile);
if (fdp.readFd == -1)
{
fdp.readFd = open (pzFile, O_RDONLY);
if (fdp.readFd < 0)
{
fprintf (stderr, "Error %d (%s) opening %s\n", errno,
strerror (errno), pzFile);
exit (EXIT_FAILURE);
}
}
for (;;)
{
int newFd = chainOpen (fdp.readFd,
(tpChar *) pFD->papzPatchArgs,
(chainHead == -1)
? &chainHead : (pid_t *) NULL);
if (newFd != -1)
{
fdp.readFd = newFd;
break;
}
fprintf (stderr, "Error %d (%s) starting filter process "
"for %s\n", errno, strerror (errno),
pFD->pzFixName);
if (errno != EAGAIN)
exit (EXIT_FAILURE);
sleep (1);
}
}
nextFix:;
}
/*
* IF after all the tests we did not start any patch programs,
* THEN quit now.
*/
if (fdp.readFd < 0)
return;
{
FILE *inFp = fdopen (fdp.readFd, "r");
FILE *oFp = (FILE *) NULL;
char *pzCmp = pzDta;
for (;;)
{
int ch;
ch = getc (inFp);
if (ch == EOF)
break;
if (oFp != (FILE *) NULL)
putc (ch, oFp);
else if (ch != *pzCmp)
{
oFp = createFile (pzFile);
if (pzCmp != pzDta)
{
char c = *pzCmp;
*pzCmp = NUL;
fputs (pzDta, oFp);
*pzCmp = c;
}
putc (ch, oFp);
}
else
pzCmp++;
}
if (oFp != (FILE *) NULL)
{
regmatch_t match;
fchmod (fileno (oFp), S_IRUSR | S_IRGRP | S_IROTH);
fclose (oFp);
if (regexec (&inclQuoteRegex, pzDta, 1, &match, 0) == 0)
extractQuotedFiles (pzDta, pzFile, &match);
}
fclose (inFp);
}
close (fdp.readFd);
}