X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=fixincludes%2Ffixincl.c;fp=fixincludes%2Ffixincl.c;h=9f399abdd83d54660726a8f44eb5c8fcfcaf8149;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=0000000000000000000000000000000000000000;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/fixincludes/fixincl.c b/fixincludes/fixincl.c new file mode 100644 index 00000000..9f399abd --- /dev/null +++ b/fixincludes/fixincl.c @@ -0,0 +1,1358 @@ +/* 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 + 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 +. */ + +#include "fixlib.h" + +#include +#include +#ifndef SEPARATE_FIX_PROC +#include +#endif + +#if defined( HAVE_MMAP_FILE ) +#include +#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'", (long) (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) + { + 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. + 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(); +}