12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421 |
- /* 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]*\"[^/]";
- 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;
- 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;
- }
- #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;
- #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
- #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;
- #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.
- "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();
- }
|