| /* |
| * $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); |
| } |