mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1474 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1474 lines
		
	
	
		
			38 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, 2009, 2012
 | |
|    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 3, 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 COPYING3.  If not see
 | |
| <http://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| #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;
 | |
| int have_tty = 0;
 | |
| 
 | |
| 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]*\"[^/]";
 | |
| regex_t incl_quote_re;
 | |
| 
 | |
| #ifndef SEPARATE_FIX_PROC
 | |
| tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
 | |
| #endif
 | |
| 
 | |
| 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_with_shell (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" );
 | |
| 
 | |
| #if defined(__MINGW32__)
 | |
|   fix_path_separators (pz_temp_file);
 | |
| #endif
 | |
| 
 | |
| # 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;
 | |
|       int test_ct;
 | |
| 
 | |
|       if (fixinc_mode && (p_fixd->fd_flags & FD_REPLACEMENT))
 | |
|         {
 | |
|           p_fixd->fd_flags |= FD_SKIP_TEST;
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|       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)
 | |
|          && ! 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;
 | |
| }
 | |
| #elif defined(__MINGW32__) || defined(__DJGPP__)
 | |
| static int
 | |
| test_test (tTestDesc* p_test, char* pz_test_file)
 | |
| {
 | |
|   tSCC cmd_fmt[] =
 | |
| #if defined(__DJGPP__)
 | |
|     "file=%s; test %s >/dev/null 2>/dev/null";
 | |
| #else
 | |
|     "file=%s; test %s > /dev/null 2>&1";
 | |
| #endif
 | |
|   int res;
 | |
| 
 | |
|   char *cmd_buf = XNEWVEC (char, strlen(cmd_fmt) + strlen(pz_test_file) + strlen(p_test->pz_test_text));
 | |
| 
 | |
|   sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
 | |
|   res = system_with_shell (cmd_buf);
 | |
| 
 | |
|   free (cmd_buf);
 | |
|   return res ? SKIP_FIX : APPLY_FIX;
 | |
| }
 | |
| #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;
 | |
| }
 | |
| 
 | |
| /* * * * * * * * * * * * *
 | |
| 
 | |
|   cksum_test   check the sum of the candidate file
 | |
|   Input:  the original file contents and the file name
 | |
|   Result: APPLY_FIX if the check sum matches, SKIP_FIX otherwise
 | |
| 
 | |
|   The caller may choose to reverse meaning if the sense of the test
 | |
|   is inverted.  */
 | |
| 
 | |
| static int
 | |
| cksum_test (char * pz_data, tTestDesc * p_test, char * fname)
 | |
| {
 | |
|   unsigned int cksum;
 | |
| 
 | |
|   /*
 | |
|    * Testing is off in normal operation mode.
 | |
|    * So, in testing mode, APPLY_FIX is always returned.
 | |
|    */
 | |
|   if (fixinc_mode != TESTING_OFF)
 | |
|     return APPLY_FIX;
 | |
| 
 | |
|   {
 | |
|     char * fnm = strrchr(fname, '/');
 | |
|     if (fnm != NULL)
 | |
|       fname = fnm + 1;
 | |
|     
 | |
|     errno = 0;
 | |
|     cksum = (unsigned int)strtoul(p_test->pz_test_text, &fnm, 10);
 | |
|     if (errno != 0)
 | |
|       return SKIP_FIX;
 | |
| 
 | |
|     if (! ISSPACE(*fnm++))
 | |
|       return SKIP_FIX;
 | |
|     while (ISSPACE(*fnm)) fnm++;
 | |
| 
 | |
|     if (! ISDIGIT(*fnm++))
 | |
|       return SKIP_FIX;
 | |
|     while (ISDIGIT(*fnm)) fnm++;
 | |
| 
 | |
|     if (! ISSPACE(*fnm++))
 | |
|       return SKIP_FIX;
 | |
|     while (ISSPACE(*fnm)) fnm++;
 | |
| 
 | |
|     if (strcmp(fnm, fname) != 0)
 | |
|       return SKIP_FIX;
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     unsigned int sum = 0;
 | |
|     while (*pz_data != NUL) {
 | |
|       sum = (sum >> 1) + ((sum & 1) << 15) + (unsigned)(*pz_data++);
 | |
|       sum &= 0xFFFF;
 | |
|     }
 | |
| 
 | |
|     return (sum == cksum) ? APPLY_FIX : 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'", (long) (p_fixd - fixDescList),
 | |
|                pz_fix_file, pz_file_source, pz_temp_file);
 | |
|     }
 | |
|   else /* NOT an "internal" fix: */
 | |
|     {
 | |
|       size_t parg_size;
 | |
| #if defined(__MSDOS__) && !defined(__DJGPP__)
 | |
|       /* 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
 | |
|       tSCC   z_subshell_start[] = "( ";
 | |
|       tSCC   z_subshell_end[] = " ) < ";
 | |
|       tCC**  ppArgs = p_fixd->patch_args;
 | |
| 
 | |
|       argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
 | |
|               + strlen( pz_file_source );
 | |
|       parg_size = argsize;
 | |
|       
 | |
|       if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
 | |
|         {
 | |
|           argsize += strlen( z_subshell_start ) + strlen ( z_subshell_end );
 | |
|         }
 | |
| 
 | |
|       /*
 | |
|        *  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;
 | |
| 
 | |
|       /*
 | |
|        * If it's shell script, enclose it in parentheses and skip "sh -c".
 | |
|        */
 | |
|       if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
 | |
|         {
 | |
|           strcpy (pz_scan, z_subshell_start);
 | |
|           pz_scan += strlen (z_subshell_start);
 | |
|           ppArgs += 2;
 | |
|         }
 | |
| 
 | |
|       /*
 | |
|        *  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) );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|       /*
 | |
|        * Close parenthesis if it's shell script.
 | |
|        */
 | |
|       if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
 | |
|         {
 | |
|           strcpy (pz_scan, z_subshell_end);
 | |
|           pz_scan += strlen (z_subshell_end);
 | |
|         }
 | |
| 
 | |
|       /*
 | |
|        *  add the file machinations.
 | |
|        */
 | |
| #if defined(__MSDOS__) && !defined(__DJGPP__)
 | |
|       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_with_shell (pz_cmd);
 | |
|   free (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
 | |
| #ifdef DEBUG
 | |
| # define NOTE_SKIP(_ttyp)  do {                                         \
 | |
|             if (VLEVEL( VERB_EVERYTHING ))                              \
 | |
|               fprintf (stderr, z_failed, _ttyp, p_fixd->fix_name,       \
 | |
|                        pz_fname, p_fixd->test_ct - test_ct);            \
 | |
|           } while (0)
 | |
| #else
 | |
| # define NOTE_SKIP(_ttyp)
 | |
| #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;
 | |
|   t_bool saw_sum_test   = BOOL_FALSE;
 | |
|   t_bool one_sum_passed = BOOL_FALSE;
 | |
| 
 | |
| #if defined(__MSDOS__) && !defined(__DJGPP__)
 | |
|   /*
 | |
|    *  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)
 | |
|     {
 | |
|       while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
 | |
|         pz_fname += 2;
 | |
| 
 | |
|       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.
 | |
|       "sum" fails only if all "sum" tests fail.
 | |
|       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) {
 | |
|             NOTE_SKIP("TEST");
 | |
|             return BOOL_FALSE;
 | |
|           }
 | |
|           break;
 | |
| 
 | |
|         case TT_EGREP:
 | |
|           if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
 | |
|             NOTE_SKIP("EGREP");
 | |
|             return BOOL_FALSE;
 | |
|           }
 | |
|           break;
 | |
| 
 | |
|         case TT_NEGREP:
 | |
|           if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
 | |
|             NOTE_SKIP("NEGREP");
 | |
|             /*  Negated sense  */
 | |
|             return BOOL_FALSE;
 | |
|           }
 | |
|           break;
 | |
| 
 | |
|         case TT_CKSUM:
 | |
| 	  if (one_sum_passed)
 | |
| 	    break; /*  No need to check any more  */
 | |
| 
 | |
|           saw_sum_test = BOOL_TRUE;
 | |
|           if (cksum_test (pz_curr_data, p_test, pz_curr_file) != APPLY_FIX) {
 | |
|             NOTE_SKIP("CKSUM");
 | |
|           } else {
 | |
|             one_sum_passed = BOOL_TRUE;
 | |
|           }
 | |
|           break;
 | |
| 
 | |
|         case TT_FUNCTION:
 | |
|           if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
 | |
|               != APPLY_FIX) {
 | |
|             NOTE_SKIP("FTEST");
 | |
|             return BOOL_FALSE;
 | |
|           }
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (saw_sum_test)
 | |
|     return one_sum_passed;
 | |
| 
 | |
|   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();
 | |
| }
 |