]> git.ipfire.org Git - thirdparty/gcc.git/blame - fixincludes/fixincl.c
inter-procedural value range propagation
[thirdparty/gcc.git] / fixincludes / fixincl.c
CommitLineData
1f414ac4
BK
1/* Install modified versions of certain ANSI-incompatible system header
2 files which are fixed to work correctly with ANSI C and placed in a
6e6a1681 3 directory that GCC will search.
1f414ac4 4
90d04a44 5 Copyright (C) 1997, 1998, 1999, 2000, 2004, 2009, 2012
748086b7 6 Free Software Foundation, Inc.
1f414ac4 7
6e6a1681 8This file is part of GCC.
1f414ac4 9
6e6a1681 10GCC is free software; you can redistribute it and/or modify
1f414ac4 11it under the terms of the GNU General Public License as published by
748086b7 12the Free Software Foundation; either version 3, or (at your option)
1f414ac4
BK
13any later version.
14
6e6a1681 15GCC is distributed in the hope that it will be useful,
1f414ac4
BK
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
748086b7 18GNU General Public License for more details.
1f414ac4
BK
19
20You should have received a copy of the GNU General Public License
748086b7
JJ
21along with GCC; see the file COPYING3. If not see
22<http://www.gnu.org/licenses/>. */
1f414ac4 23
5abc1f74
BK
24#include "fixlib.h"
25
401be4b6 26#include <fnmatch.h>
ad643a75 27#include <sys/stat.h>
8ba8f7e5 28#ifndef SEPARATE_FIX_PROC
03a9fcb8 29#include <sys/wait.h>
8ba8f7e5 30#endif
ad643a75 31
1e570a6a 32#if defined( HAVE_MMAP_FILE )
99d525c9
PDM
33#include <sys/mman.h>
34#define BAD_ADDR ((void*)-1)
35#endif
1f414ac4 36
283da1d3 37#ifndef SEPARATE_FIX_PROC
0083c904 38#include "server.h"
62a99405 39#endif
0083c904 40
5abc1f74
BK
41/* The contents of this string are not very important. It is mostly
42 just used as part of the "I am alive and working" test. */
0083c904 43
5abc1f74 44static const char program_id[] = "fixincl version 1.1";
0083c904 45
e02ecf39
BK
46/* This format will be used at the start of every generated file */
47
48static const char z_std_preamble[] =
49"/* DO NOT EDIT THIS FILE.\n\n\
50 It has been auto-edited by fixincludes from:\n\n\
51\t\"%s/%s\"\n\n\
52 This had to be done to correct non-standard usages in the\n\
53 original, manufacturer supplied header file. */\n\n";
54
7d9ccd90 55int find_base_len = 0;
d7eb5a45 56int have_tty = 0;
b35926b9 57
1f414ac4
BK
58pid_t process_chain_head = (pid_t) -1;
59
5abc1f74
BK
60char* pz_curr_file; /* name of the current file under test/fix */
61char* pz_curr_data; /* original contents of that file */
62a99405
BK
62char* pz_temp_file; /* for DOS, a place to stash the temporary
63 fixed data between system(3) calls */
5abc1f74
BK
64t_bool curr_data_mapped;
65int data_map_fd;
66size_t data_map_size;
67size_t ttl_data_size = 0;
e02ecf39 68
5abc1f74
BK
69#ifdef DO_STATS
70int process_ct = 0;
71int apply_ct = 0;
72int fixed_ct = 0;
73int altered_ct = 0;
74#endif /* DO_STATS */
75
1f414ac4
BK
76const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
77regex_t incl_quote_re;
78
716028e4
TK
79#ifndef SEPARATE_FIX_PROC
80tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
81#endif
82
f4dbf936
NN
83static void do_version (void) ATTRIBUTE_NORETURN;
84char *load_file (const char *);
85void run_compiles (void);
86void initialize (int argc, char** argv);
87void process (void);
5abc1f74
BK
88
89/* External Source Code */
0083c904
BK
90
91#include "fixincl.x"
92
1f414ac4
BK
93/* * * * * * * * * * * * * * * * * * *
94 *
95 * MAIN ROUTINE
96 */
f4dbf936 97extern int main (int, char **);
0083c904 98int
f4dbf936 99main (int argc, char** argv)
0083c904 100{
5abc1f74 101 char *file_name_buf;
0083c904 102
35dfe415 103 initialize ( argc, argv );
d1c6a037 104
d7eb5a45
ZW
105 have_tty = isatty (fileno (stderr));
106
5abc1f74
BK
107 /* Before anything else, ensure we can allocate our file name buffer. */
108 file_name_buf = load_file_data (stdin);
109
110 /* Because of the way server shells work, you have to keep stdin, out
111 and err open so that the proper input file does not get closed
112 by accident */
113
114 freopen ("/dev/null", "r", stdin);
115
116 if (file_name_buf == (char *) NULL)
117 {
118 fputs ("No file names listed for fixing\n", stderr);
119 exit (EXIT_FAILURE);
120 }
121
d1c6a037
BK
122 for (;;)
123 {
5abc1f74 124 char* pz_end;
d1c6a037 125
5abc1f74 126 /* skip to start of name, past any "./" prefixes */
d1c6a037 127
5abc1f74
BK
128 while (ISSPACE (*file_name_buf)) file_name_buf++;
129 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
130 file_name_buf += 2;
d1c6a037 131
5abc1f74
BK
132 /* Check for end of list */
133
134 if (*file_name_buf == NUL)
135 break;
136
137 /* Set global file name pointer and find end of name */
138
139 pz_curr_file = file_name_buf;
140 pz_end = strchr( pz_curr_file, '\n' );
141 if (pz_end == (char*)NULL)
142 pz_end = file_name_buf = pz_curr_file + strlen (pz_curr_file);
143 else
144 file_name_buf = pz_end + 1;
145
146 while ((pz_end > pz_curr_file) && ISSPACE( pz_end[-1])) pz_end--;
147
148 /* IF no name is found (blank line) or comment marker, skip line */
149
150 if ((pz_curr_file == pz_end) || (*pz_curr_file == '#'))
151 continue;
152 *pz_end = NUL;
d1c6a037 153
5abc1f74 154 process ();
5abc1f74 155 } /* for (;;) */
d1c6a037 156
5abc1f74 157#ifdef DO_STATS
b35926b9 158 if (VLEVEL( VERB_PROGRESS )) {
5abc1f74
BK
159 tSCC zFmt[] =
160 "\
161Processed %5d files containing %d bytes \n\
162Applying %5d fixes to %d files\n\
163Altering %5d of them\n";
164
165 fprintf (stderr, zFmt, process_ct, ttl_data_size, apply_ct,
166 fixed_ct, altered_ct);
167 }
168#endif /* DO_STATS */
62a99405 169
283da1d3 170# ifdef SEPARATE_FIX_PROC
62a99405
BK
171 unlink( pz_temp_file );
172# endif
15fe1a7e 173 exit (EXIT_SUCCESS);
d1c6a037
BK
174}
175
176
6864a6c6 177static void
f4dbf936 178do_version (void)
5abc1f74
BK
179{
180 static const char zFmt[] = "echo '%s'";
181 char zBuf[ 1024 ];
182
183 /* The 'version' option is really used to test that:
184 1. The program loads correctly (no missing libraries)
35dfe415
BK
185 2. that we can compile all the regular expressions.
186 3. we can correctly run our server shell process
5abc1f74
BK
187 */
188 run_compiles ();
189 sprintf (zBuf, zFmt, program_id);
283da1d3 190#ifndef SEPARATE_FIX_PROC
6b151aa7 191 puts (zBuf + 5);
5abc1f74 192 exit (strcmp (run_shell (zBuf), program_id));
62a99405 193#else
716028e4 194 exit (system_with_shell (zBuf));
62a99405 195#endif
5abc1f74
BK
196}
197
d1c6a037
BK
198/* * * * * * * * * * * * */
199
200void
f4dbf936 201initialize ( int argc, char** argv )
d1c6a037 202{
878a5794
BK
203 xmalloc_set_program_name (argv[0]);
204
35dfe415
BK
205 switch (argc)
206 {
207 case 1:
208 break;
209
210 case 2:
211 if (strcmp (argv[1], "-v") == 0)
212 do_version ();
213 if (freopen (argv[1], "r", stdin) == (FILE*)NULL)
214 {
215 fprintf (stderr, "Error %d (%s) reopening %s as stdin\n",
216 errno, xstrerror (errno), argv[1] );
217 exit (EXIT_FAILURE);
218 }
219 break;
220
221 default:
222 fputs ("fixincl ERROR: too many command line arguments\n", stderr);
223 exit (EXIT_FAILURE);
224 }
225
798bdf70
BK
226#ifdef SIGCHLD
227 /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
228 receive the signal. A different setting is inheritable */
229 signal (SIGCHLD, SIG_DFL);
230#endif
231
ad643a75 232 initialize_opts ();
0083c904 233
6864a6c6 234 if (ISDIGIT ( *pz_verbose ))
e02ecf39
BK
235 verbose_level = (te_verbose)atoi( pz_verbose );
236 else
237 switch (*pz_verbose) {
238 case 's':
239 case 'S':
240 verbose_level = VERB_SILENT; break;
241
242 case 'f':
243 case 'F':
244 verbose_level = VERB_FIXES; break;
245
246 case 'a':
247 case 'A':
248 verbose_level = VERB_APPLIES; break;
249
7a9cdb10 250 default:
e02ecf39
BK
251 case 'p':
252 case 'P':
253 verbose_level = VERB_PROGRESS; break;
254
255 case 't':
256 case 'T':
257 verbose_level = VERB_TESTS; break;
258
259 case 'e':
260 case 'E':
261 verbose_level = VERB_EVERYTHING; break;
262 }
7a9cdb10
DD
263 if (verbose_level >= VERB_EVERYTHING) {
264 verbose_level = VERB_EVERYTHING;
265 fputs ("fixinc verbosity: EVERYTHING\n", stderr);
266 }
267 while ((pz_find_base[0] == '.') && (pz_find_base[1] == '/'))
268 pz_find_base += 2;
269 if ((pz_find_base[0] != '.') || (pz_find_base[1] != NUL))
270 find_base_len = strlen( pz_find_base );
7d9ccd90 271
1f414ac4
BK
272 /* Compile all the regular expressions now.
273 That way, it is done only once for the whole run.
274 */
275 run_compiles ();
0083c904 276
283da1d3 277# ifdef SEPARATE_FIX_PROC
62a99405
BK
278 /* NULL as the first argument to `tempnam' causes it to DTRT
279 wrt the temporary directory where the file will be created. */
280 pz_temp_file = tempnam( NULL, "fxinc" );
716028e4
TK
281
282#if defined(__MINGW32__)
283 fix_path_separators (pz_temp_file);
284#endif
285
62a99405
BK
286# endif
287
1f414ac4
BK
288 signal (SIGQUIT, SIG_IGN);
289 signal (SIGIOT, SIG_IGN);
290 signal (SIGPIPE, SIG_IGN);
291 signal (SIGALRM, SIG_IGN);
292 signal (SIGTERM, SIG_IGN);
0083c904 293}
0083c904 294
1f414ac4 295/* * * * * * * * * * * * *
5abc1f74 296
1f414ac4
BK
297 load_file loads all the contents of a file into malloc-ed memory.
298 Its argument is the name of the file to read in; the returned
299 result is the NUL terminated contents of the file. The file
300 is presumed to be an ASCII text file containing no NULs. */
0083c904 301char *
f4dbf936 302load_file ( const char* fname )
0083c904 303{
5abc1f74
BK
304 struct stat stbf;
305 char* res;
0083c904 306
5abc1f74 307 if (stat (fname, &stbf) != 0)
0083c904 308 {
b35926b9
BK
309 if (NOT_SILENT)
310 fprintf (stderr, "error %d (%s) stat-ing %s\n",
f95e46b9 311 errno, xstrerror (errno), fname );
5abc1f74 312 return (char *) NULL;
0083c904 313 }
5abc1f74
BK
314 if (stbf.st_size == 0)
315 return (char*)NULL;
0083c904 316
a6efbece
BK
317 /* Make the data map size one larger than the file size for documentation
318 purposes. Truth is that there will be a following NUL character if
319 the file size is not a multiple of the page size. If it is a multiple,
320 then this adjustment sometimes fails anyway. */
5abc1f74
BK
321 data_map_size = stbf.st_size+1;
322 data_map_fd = open (fname, O_RDONLY);
323 ttl_data_size += data_map_size-1;
0083c904 324
5abc1f74
BK
325 if (data_map_fd < 0)
326 {
b35926b9
BK
327 if (NOT_SILENT)
328 fprintf (stderr, "error %d (%s) opening %s for read\n",
f95e46b9 329 errno, xstrerror (errno), fname);
5abc1f74
BK
330 return (char*)NULL;
331 }
0083c904 332
56f02b88 333#ifdef HAVE_MMAP_FILE
5abc1f74 334 curr_data_mapped = BOOL_TRUE;
a6efbece
BK
335
336 /* IF the file size is a multiple of the page size,
337 THEN sometimes you will seg fault trying to access a trailing byte */
dc46505a 338 if ((stbf.st_size & (getpagesize()-1)) == 0)
a6efbece
BK
339 res = (char*)BAD_ADDR;
340 else
341 res = (char*)mmap ((void*)NULL, data_map_size, PROT_READ,
342 MAP_PRIVATE, data_map_fd, 0);
5abc1f74 343 if (res == (char*)BAD_ADDR)
ca21b4a5 344#endif
5abc1f74 345 {
ca21b4a5 346 FILE* fp = fdopen (data_map_fd, "r");
5abc1f74 347 curr_data_mapped = BOOL_FALSE;
ca21b4a5
BK
348 res = load_file_data (fp);
349 fclose (fp);
5abc1f74 350 }
0083c904 351
5abc1f74 352 return res;
0083c904
BK
353}
354
6864a6c6 355static int
f4dbf936 356machine_matches( tFixDesc* p_fixd )
401be4b6
BK
357{
358 char const ** papz_machs = p_fixd->papz_machs;
359 int have_match = BOOL_FALSE;
1f414ac4 360
62a99405
BK
361 for (;;)
362 {
401be4b6
BK
363 char const * pz_mpat = *(papz_machs++);
364 if (pz_mpat == NULL)
62a99405 365 break;
401be4b6
BK
366 if (fnmatch(pz_mpat, pz_machine, 0) == 0)
367 {
368 have_match = BOOL_TRUE;
369 break;
370 }
62a99405
BK
371 }
372
2f82a97b 373 /* Check for sense inversion then set the "skip test" flag, if needed */
401be4b6 374 if (p_fixd->fd_flags & FD_MACH_IFNOT)
2f82a97b
BK
375 have_match = ! have_match;
376
377 if (! have_match)
378 p_fixd->fd_flags |= FD_SKIP_TEST;
379
401be4b6 380 return have_match;
62a99405
BK
381}
382
383/* * * * * * * * * * * * *
401be4b6
BK
384 *
385 * run_compiles run all the regexp compiles for all the fixes once.
386 */
62a99405 387void
f4dbf936 388run_compiles (void)
62a99405
BK
389{
390 tFixDesc *p_fixd = fixDescList;
391 int fix_ct = FIX_COUNT;
03a9fcb8 392 regex_t *p_re = XCNEWVEC (regex_t, REGEX_COUNT);
62a99405
BK
393
394 /* Make sure compile_re does not stumble across invalid data */
395
c68b0a84 396 memset (&incl_quote_re, '\0', sizeof (regex_t));
62a99405
BK
397
398 compile_re (incl_quote_pat, &incl_quote_re, 1,
399 "quoted include", "run_compiles");
400
401 /* Allow machine name tests to be ignored (testing, mainly) */
402
403 if (pz_machine && ((*pz_machine == '\0') || (*pz_machine == '*')))
404 pz_machine = (char*)NULL;
405
406 /* FOR every fixup, ... */
407 do
408 {
7e416541
BK
409 tTestDesc *p_test;
410 int test_ct;
411
412 if (fixinc_mode && (p_fixd->fd_flags & FD_REPLACEMENT))
413 {
414 p_fixd->fd_flags |= FD_SKIP_TEST;
415 continue;
416 }
417
418 p_test = p_fixd->p_test_desc;
419 test_ct = p_fixd->test_ct;
62a99405
BK
420
421 /* IF the machine type pointer is not NULL (we are not in test mode)
422 AND this test is for or not done on particular machines
423 THEN ... */
424
425 if ( (pz_machine != NULL)
426 && (p_fixd->papz_machs != (const char**) NULL)
427 && ! machine_matches (p_fixd) )
428 continue;
0083c904 429
1f414ac4
BK
430 /* FOR every test for the fixup, ... */
431
432 while (--test_ct >= 0)
0083c904 433 {
1f414ac4 434 switch (p_test->type)
0083c904
BK
435 {
436 case TT_EGREP:
437 case TT_NEGREP:
1f414ac4 438 p_test->p_test_regex = p_re++;
35dfe415
BK
439 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
440 "select test", p_fixd->fix_name);
3af556f7 441 default: break;
35dfe415 442 }
1f414ac4 443 p_test++;
0083c904
BK
444 }
445 }
1f414ac4 446 while (p_fixd++, --fix_ct > 0);
0083c904
BK
447}
448
449
1f414ac4 450/* * * * * * * * * * * * *
5abc1f74 451
1f414ac4
BK
452 create_file Create the output modified file.
453 Input: the name of the file to create
454 Returns: a file pointer to the new, open file */
455
063174ee
BK
456#if defined(S_IRUSR) && defined(S_IWUSR) && \
457 defined(S_IRGRP) && defined(S_IROTH)
458
459# define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
460#else
461# define S_IRALL 0644
462#endif
463
464#if defined(S_IRWXU) && defined(S_IRGRP) && defined(S_IXGRP) && \
465 defined(S_IROTH) && defined(S_IXOTH)
466
467# define S_DIRALL (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
468#else
469# define S_DIRALL 0755
470#endif
471
1f414ac4 472
6864a6c6 473static FILE *
f4dbf936 474create_file (void)
0083c904
BK
475{
476 int fd;
477 FILE *pf;
478 char fname[MAXPATHLEN];
479
5abc1f74 480 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
0083c904 481
1f414ac4 482 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
0083c904 483
1f414ac4 484 /* We may need to create the directories needed... */
0083c904
BK
485 if ((fd < 0) && (errno == ENOENT))
486 {
1f414ac4 487 char *pz_dir = strchr (fname + 1, '/');
0083c904
BK
488 struct stat stbf;
489
1f414ac4 490 while (pz_dir != (char *) NULL)
0083c904 491 {
1f414ac4 492 *pz_dir = NUL;
0083c904
BK
493 if (stat (fname, &stbf) < 0)
494 {
f4a8f279
AL
495#ifdef _WIN32
496 mkdir (fname);
497#else
063174ee 498 mkdir (fname, S_IFDIR | S_DIRALL);
f4a8f279 499#endif
0083c904
BK
500 }
501
1f414ac4
BK
502 *pz_dir = '/';
503 pz_dir = strchr (pz_dir + 1, '/');
0083c904 504 }
1f414ac4
BK
505
506 /* Now, lets try the open again... */
507 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
0083c904
BK
508 }
509 if (fd < 0)
510 {
511 fprintf (stderr, "Error %d (%s) creating %s\n",
f95e46b9 512 errno, xstrerror (errno), fname);
0083c904
BK
513 exit (EXIT_FAILURE);
514 }
b35926b9
BK
515 if (NOT_SILENT)
516 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
0083c904
BK
517 pf = fdopen (fd, "w");
518
e02ecf39
BK
519 /*
520 * IF pz_machine is NULL, then we are in some sort of test mode.
521 * Do not insert the current directory name. Use a constant string.
522 */
523 fprintf (pf, z_std_preamble,
524 (pz_machine == NULL)
525 ? "fixinc/tests/inc"
526 : pz_input_dir,
527 pz_curr_file);
1f414ac4 528
0083c904
BK
529 return pf;
530}
531
0083c904 532
1f414ac4 533/* * * * * * * * * * * * *
0083c904 534
1f414ac4
BK
535 test_test make sure a shell-style test expression passes.
536 Input: a pointer to the descriptor of the test to run and
537 the name of the file that we might want to fix
5abc1f74 538 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
1f414ac4 539 shell script we run. */
283da1d3 540#ifndef SEPARATE_FIX_PROC
6864a6c6 541static int
f4dbf936 542test_test (tTestDesc* p_test, char* pz_test_file)
1f414ac4 543{
94db2f71
BK
544 tSCC cmd_fmt[] =
545"file=%s\n\
546if ( test %s ) > /dev/null 2>&1\n\
547then echo TRUE\n\
548else echo FALSE\n\
549fi";
550
1f414ac4 551 char *pz_res;
e9099386 552 int res;
1f414ac4
BK
553
554 static char cmd_buf[4096];
1f414ac4 555
5abc1f74 556 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
1f414ac4 557 pz_res = run_shell (cmd_buf);
e9099386
BK
558
559 switch (*pz_res) {
560 case 'T':
5abc1f74 561 res = APPLY_FIX;
e9099386
BK
562 break;
563
564 case 'F':
565 res = SKIP_FIX;
566 break;
567
568 default:
569 fprintf (stderr, "Script yielded bogus result of `%s':\n%s\n\n",
570 pz_res, cmd_buf );
9cdfc8e7 571 res = SKIP_FIX;
e9099386
BK
572 }
573
1f414ac4 574 free ((void *) pz_res);
0083c904
BK
575 return res;
576}
716028e4
TK
577#elif defined(__MINGW32__) || defined(__DJGPP__)
578static int
579test_test (tTestDesc* p_test, char* pz_test_file)
580{
581 tSCC cmd_fmt[] =
582#if defined(__DJGPP__)
583 "file=%s; test %s >/dev/null 2>/dev/null";
584#else
585 "file=%s; test %s > /dev/null 2>&1";
586#endif
587 int res;
588
589 char *cmd_buf = XNEWVEC (char, strlen(cmd_fmt) + strlen(pz_test_file) + strlen(p_test->pz_test_text));
590
591 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
592 res = system_with_shell (cmd_buf);
593
594 free (cmd_buf);
595 return res ? SKIP_FIX : APPLY_FIX;
596}
62a99405
BK
597#else
598/*
599 * IF we are in MS-DOS land, then whatever shell-type test is required
600 * will, by definition, fail
601 */
602#define test_test(t,tf) SKIP_FIX
603#endif
0083c904 604
1f414ac4 605/* * * * * * * * * * * * *
5abc1f74 606
1f414ac4
BK
607 egrep_test make sure an egrep expression is found in the file text.
608 Input: a pointer to the descriptor of the test to run and
609 the pointer to the contents of the file under suspicion
5abc1f74 610 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
1f414ac4 611
5abc1f74 612 The caller may choose to reverse meaning if the sense of the test
1f414ac4
BK
613 is inverted. */
614
6864a6c6 615static int
f4dbf936 616egrep_test (char* pz_data, tTestDesc* p_test)
0083c904 617{
5abc1f74 618#ifdef DEBUG
1f414ac4
BK
619 if (p_test->p_test_regex == 0)
620 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
621 p_test->pz_test_text);
0083c904 622#endif
ab747408 623 if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
5abc1f74
BK
624 return APPLY_FIX;
625 return SKIP_FIX;
0083c904
BK
626}
627
11c3a68e
BK
628/* * * * * * * * * * * * *
629
630 cksum_test check the sum of the candidate file
631 Input: the original file contents and the file name
632 Result: APPLY_FIX if the check sum matches, SKIP_FIX otherwise
633
634 The caller may choose to reverse meaning if the sense of the test
635 is inverted. */
636
637static int
638cksum_test (char * pz_data, tTestDesc * p_test, char * fname)
639{
640 unsigned int cksum;
641
642 /*
643 * Testing is off in normal operation mode.
644 * So, in testing mode, APPLY_FIX is always returned.
645 */
646 if (fixinc_mode != TESTING_OFF)
647 return APPLY_FIX;
648
649 {
650 char * fnm = strrchr(fname, '/');
651 if (fnm != NULL)
652 fname = fnm + 1;
653
654 errno = 0;
655 cksum = (unsigned int)strtoul(p_test->pz_test_text, &fnm, 10);
656 if (errno != 0)
657 return SKIP_FIX;
658
659 if (! ISSPACE(*fnm++))
660 return SKIP_FIX;
661 while (ISSPACE(*fnm)) fnm++;
662
663 if (! ISDIGIT(*fnm++))
664 return SKIP_FIX;
665 while (ISDIGIT(*fnm)) fnm++;
666
667 if (! ISSPACE(*fnm++))
668 return SKIP_FIX;
669 while (ISSPACE(*fnm)) fnm++;
670
671 if (strcmp(fnm, fname) != 0)
672 return SKIP_FIX;
673 }
674
675 {
676 unsigned int sum = 0;
677 while (*pz_data != NUL) {
678 sum = (sum >> 1) + ((sum & 1) << 15) + (unsigned)(*pz_data++);
679 sum &= 0xFFFF;
680 }
681
682 return (sum == cksum) ? APPLY_FIX : SKIP_FIX;
683 }
684}
0083c904 685
94db2f71
BK
686/* * * * * * * * * * * * *
687
688 quoted_file_exists Make sure that a file exists before we emit
689 the file name. If we emit the name, our invoking shell will try
690 to copy a non-existing file into the destination directory. */
691
6864a6c6 692static int
f4dbf936
NN
693quoted_file_exists (const char* pz_src_path,
694 const char* pz_file_path,
695 const char* pz_file)
94db2f71
BK
696{
697 char z[ MAXPATHLEN ];
698 char* pz;
699 sprintf (z, "%s/%s/", pz_src_path, pz_file_path);
700 pz = z + strlen ( z );
701
702 for (;;) {
703 char ch = *pz_file++;
5abc1f74 704 if (! ISGRAPH( ch ))
94db2f71
BK
705 return 0;
706 if (ch == '"')
707 break;
708 *pz++ = ch;
709 }
710 *pz = '\0';
711 {
712 struct stat s;
713 if (stat (z, &s) != 0)
714 return 0;
715 return S_ISREG( s.st_mode );
716 }
717}
718
719
1f414ac4
BK
720/* * * * * * * * * * * * *
721 *
722 extract_quoted_files
5abc1f74 723
1f414ac4
BK
724 The syntax, `#include "file.h"' specifies that the compiler is to
725 search the local directory of the current file before the include
726 list. Consequently, if we have modified a header and stored it in
727 another directory, any files that are included by that modified
728 file in that fashion must also be copied into this new directory.
729 This routine finds those flavors of #include and for each one found
730 emits a triple of:
5abc1f74 731
1f414ac4
BK
732 1. source directory of the original file
733 2. the relative path file name of the #includ-ed file
734 3. the full destination path for this file
735
736 Input: the text of the file, the file name and a pointer to the
737 match list where the match information was stored.
738 Result: internally nothing. The results are written to stdout
739 for interpretation by the invoking shell */
0083c904 740
94db2f71 741
6864a6c6 742static void
f4dbf936
NN
743extract_quoted_files (char* pz_data,
744 const char* pz_fixed_file,
745 regmatch_t* p_re_match)
0083c904 746{
5abc1f74 747 char *pz_dir_end = strrchr (pz_fixed_file, '/');
1f414ac4 748 char *pz_incl_quot = pz_data;
0083c904 749
b35926b9
BK
750 if (VLEVEL( VERB_APPLIES ))
751 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
0083c904 752
5abc1f74 753 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
94db2f71 754 If there is none, then it is in our current directory, ".". */
1f414ac4
BK
755
756 if (pz_dir_end == (char *) NULL)
5abc1f74 757 pz_fixed_file = ".";
0083c904 758 else
1f414ac4 759 *pz_dir_end = '\0';
0083c904
BK
760
761 for (;;)
762 {
1f414ac4
BK
763 pz_incl_quot += p_re_match->rm_so;
764
765 /* Skip forward to the included file name */
f6bbde28 766 while (*pz_incl_quot != '"')
1f414ac4 767 pz_incl_quot++;
1f414ac4 768
5abc1f74 769 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
94db2f71
BK
770 {
771 /* Print the source directory and the subdirectory
772 of the file in question. */
5abc1f74 773 printf ("%s %s/", pz_src_dir, pz_fixed_file);
94db2f71
BK
774 pz_dir_end = pz_incl_quot;
775
776 /* Append to the directory the relative path of the desired file */
777 while (*pz_incl_quot != '"')
778 putc (*pz_incl_quot++, stdout);
779
780 /* Now print the destination directory appended with the
781 relative path of the desired file */
5abc1f74 782 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
94db2f71
BK
783 while (*pz_dir_end != '"')
784 putc (*pz_dir_end++, stdout);
785
786 /* End of entry */
787 putc ('\n', stdout);
788 }
0083c904 789
1f414ac4 790 /* Find the next entry */
ab747408 791 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
0083c904
BK
792 break;
793 }
794}
795
796
5abc1f74
BK
797/* * * * * * * * * * * * *
798
799 Somebody wrote a *_fix subroutine that we must call.
800 */
283da1d3 801#ifndef SEPARATE_FIX_PROC
6864a6c6 802static int
f4dbf936 803internal_fix (int read_fd, tFixDesc* p_fixd)
5abc1f74
BK
804{
805 int fd[2];
806
807 if (pipe( fd ) != 0)
808 {
809 fprintf (stderr, "Error %d on pipe(2) call\n", errno );
810 exit (EXIT_FAILURE);
811 }
812
813 for (;;)
814 {
815 pid_t childid = fork();
816
817 switch (childid)
818 {
819 case -1:
820 break;
821
822 case 0:
823 close (fd[0]);
824 goto do_child_task;
825
826 default:
827 /*
828 * Parent process
829 */
830 close (read_fd);
831 close (fd[1]);
832 return fd[0];
833 }
834
835 /*
836 * Parent in error
837 */
f95e46b9 838 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
5abc1f74
BK
839 p_fixd->fix_name);
840 {
841 static int failCt = 0;
842 if ((errno != EAGAIN) || (++failCt > 10))
843 exit (EXIT_FAILURE);
844 sleep (1);
845 }
846 } do_child_task:;
847
848 /*
849 * Close our current stdin and stdout
850 */
851 close (STDIN_FILENO);
852 close (STDOUT_FILENO);
853 UNLOAD_DATA();
854
855 /*
856 * Make the fd passed in the stdin, and the write end of
857 * the new pipe become the stdout.
858 */
f4a8f279
AL
859 dup2 (fd[1], STDOUT_FILENO);
860 dup2 (read_fd, STDIN_FILENO);
5abc1f74 861
35dfe415 862 apply_fix (p_fixd, pz_curr_file);
5abc1f74
BK
863 exit (0);
864}
283da1d3 865#endif /* !SEPARATE_FIX_PROC */
62a99405
BK
866
867
283da1d3 868#ifdef SEPARATE_FIX_PROC
62a99405 869static void
f4dbf936
NN
870fix_with_system (tFixDesc* p_fixd,
871 tCC* pz_fix_file,
872 tCC* pz_file_source,
873 tCC* pz_temp_file)
62a99405
BK
874{
875 char* pz_cmd;
876 char* pz_scan;
877 size_t argsize;
878
879 if (p_fixd->fd_flags & FD_SUBROUTINE)
43c1b5d2
BK
880 {
881 static const char z_applyfix_prog[] =
11c3a68e 882 "/../fixincludes/applyfix" EXE_EXT;
43c1b5d2
BK
883
884 struct stat buf;
885 argsize = 32
886 + strlen (pz_orig_dir)
887 + sizeof (z_applyfix_prog)
888 + strlen (pz_fix_file)
889 + strlen (pz_file_source)
890 + strlen (pz_temp_file);
891
892 /* Allocate something sure to be big enough for our purposes */
03a9fcb8 893 pz_cmd = XNEWVEC (char, argsize);
43c1b5d2
BK
894 strcpy (pz_cmd, pz_orig_dir);
895 pz_scan = pz_cmd + strlen (pz_orig_dir);
896
897 strcpy (pz_scan, z_applyfix_prog);
898
899 /* IF we can't find the "applyfix" executable file at the first guess,
11c3a68e 900 try one level higher up */
43c1b5d2 901 if (stat (pz_cmd, &buf) == -1)
11c3a68e
BK
902 {
903 strcpy (pz_scan, "/..");
904 strcpy (pz_scan+3, z_applyfix_prog);
905 }
43c1b5d2
BK
906
907 pz_scan += strlen (pz_scan);
908
909 /*
910 * Now add the fix number and file names that may be needed
911 */
c1041240 912 sprintf (pz_scan, " %ld '%s' '%s' '%s'", (long) (p_fixd - fixDescList),
11c3a68e 913 pz_fix_file, pz_file_source, pz_temp_file);
43c1b5d2 914 }
62a99405
BK
915 else /* NOT an "internal" fix: */
916 {
917 size_t parg_size;
716028e4 918#if defined(__MSDOS__) && !defined(__DJGPP__)
62a99405 919 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
283da1d3
DB
920 dst is a temporary file anyway, so we know there's no other
921 file by that name; and DOS's system(3) doesn't mind to
dc13bad7
EZ
922 clobber existing file in redirection. Besides, with DOS 8+3
923 limited file namespace, we can easily lose if dst already has
924 an extension that is 3 or more characters long.
283da1d3
DB
925
926 I do not think the 8+3 issue is relevant because all the files
927 we operate on are named "*.h", making 8+2 adequate. Anyway,
928 the following bizarre use of 'cat' only works on DOS boxes.
929 It causes the file to be dropped into a temporary file for
dc13bad7 930 'cat' to read (pipes do not work on DOS). */
ecb0d20e 931 tSCC z_cmd_fmt[] = " '%s' | cat > '%s'";
283da1d3
DB
932#else
933 /* Don't use positional formatting arguments because some lame-o
934 implementations cannot cope :-(. */
935 tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s";
936#endif
716028e4
TK
937 tSCC z_subshell_start[] = "( ";
938 tSCC z_subshell_end[] = " ) < ";
62a99405
BK
939 tCC** ppArgs = p_fixd->patch_args;
940
941 argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file )
942 + strlen( pz_file_source );
943 parg_size = argsize;
944
716028e4
TK
945 if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
946 {
947 argsize += strlen( z_subshell_start ) + strlen ( z_subshell_end );
948 }
62a99405
BK
949
950 /*
951 * Compute the size of the command line. Add lotsa extra space
952 * because some of the args to sed use lotsa single quotes.
953 * (This requires three extra bytes per quote. Here we allow
954 * for up to 8 single quotes for each argument, including the
955 * command name "sed" itself. Nobody will *ever* need more. :)
956 */
957 for (;;)
958 {
959 tCC* p_arg = *(ppArgs++);
960 if (p_arg == NULL)
961 break;
962 argsize += 24 + strlen( p_arg );
963 }
964
965 /* Estimated buffer size we will need. */
03a9fcb8 966 pz_scan = pz_cmd = XNEWVEC (char, argsize);
62a99405
BK
967 /* How much of it do we allot to the program name and its
968 arguments. */
969 parg_size = argsize - parg_size;
970
971 ppArgs = p_fixd->patch_args;
972
716028e4
TK
973 /*
974 * If it's shell script, enclose it in parentheses and skip "sh -c".
975 */
976 if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
977 {
978 strcpy (pz_scan, z_subshell_start);
979 pz_scan += strlen (z_subshell_start);
980 ppArgs += 2;
981 }
982
62a99405
BK
983 /*
984 * Copy the program name, unquoted
985 */
986 {
987 tCC* pArg = *(ppArgs++);
988 for (;;)
989 {
990 char ch = *(pArg++);
991 if (ch == NUL)
992 break;
993 *(pz_scan++) = ch;
994 }
995 }
996
997 /*
998 * Copy the program arguments, quoted
999 */
1000 for (;;)
1001 {
1002 tCC* pArg = *(ppArgs++);
11c3a68e 1003 char* pz_scan_save;
62a99405
BK
1004 if (pArg == NULL)
1005 break;
1006 *(pz_scan++) = ' ';
1007 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
11c3a68e
BK
1008 parg_size - (pz_scan - pz_cmd) );
1009 /*
1010 * Make sure we don't overflow the buffer due to sloppy
1011 * size estimation.
1012 */
1013 while (pz_scan == (char*)NULL)
1014 {
1015 size_t already_filled = pz_scan_save - pz_cmd;
1016 pz_cmd = xrealloc (pz_cmd, argsize += 100);
1017 pz_scan_save = pz_scan = pz_cmd + already_filled;
1018 parg_size += 100;
1019 pz_scan = make_raw_shell_str( pz_scan, pArg,
1020 parg_size - (pz_scan - pz_cmd) );
1021 }
62a99405
BK
1022 }
1023
716028e4
TK
1024 /*
1025 * Close parenthesis if it's shell script.
1026 */
1027 if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
1028 {
1029 strcpy (pz_scan, z_subshell_end);
1030 pz_scan += strlen (z_subshell_end);
1031 }
1032
62a99405
BK
1033 /*
1034 * add the file machinations.
1035 */
716028e4 1036#if defined(__MSDOS__) && !defined(__DJGPP__)
283da1d3
DB
1037 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file );
1038#else
1039 sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file,
1040 pz_temp_file, pz_temp_file, pz_temp_file);
1041#endif
62a99405 1042 }
716028e4
TK
1043 system_with_shell (pz_cmd);
1044 free (pz_cmd);
62a99405 1045}
bb786201
BK
1046
1047/* * * * * * * * * * * * *
1048
1049 This loop should only cycle for 1/2 of one loop.
1050 "chain_open" starts a process that uses "read_fd" as
1051 its stdin and returns the new fd this process will use
1052 for stdout. */
1053
283da1d3 1054#else /* is *NOT* SEPARATE_FIX_PROC */
6864a6c6 1055static int
f4dbf936 1056start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
bb786201 1057{
bb786201
BK
1058 tCC* pz_cmd_save;
1059 char* pz_cmd;
1060
5abc1f74
BK
1061 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
1062 return internal_fix (read_fd, p_fixd);
1063
bb786201 1064 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
9cdfc8e7
JM
1065 {
1066 pz_cmd = NULL;
1067 pz_cmd_save = NULL;
1068 }
bb786201
BK
1069 else
1070 {
1071 tSCC z_cmd_fmt[] = "file='%s'\n%s";
03a9fcb8 1072 pz_cmd = XNEWVEC (char, strlen (p_fixd->patch_args[2])
11c3a68e 1073 + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
5abc1f74 1074 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
bb786201
BK
1075 pz_cmd_save = p_fixd->patch_args[2];
1076 p_fixd->patch_args[2] = pz_cmd;
1077 }
1078
b35926b9
BK
1079 /* Start a fix process, handing off the previous read fd for its
1080 stdin and getting a new fd that reads from the fix process' stdout.
1081 We normally will not loop, but we will up to 10 times if we keep
1082 getting "EAGAIN" errors.
1083
1084 */
bb786201
BK
1085 for (;;)
1086 {
1087 static int failCt = 0;
1088 int fd;
1089
1090 fd = chain_open (read_fd,
2629a114 1091 (tCC **) p_fixd->patch_args,
bb786201
BK
1092 (process_chain_head == -1)
1093 ? &process_chain_head : (pid_t *) NULL);
1094
1095 if (fd != -1)
1096 {
1097 read_fd = fd;
1098 break;
1099 }
1100
f95e46b9 1101 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
bb786201
BK
1102 p_fixd->fix_name);
1103
1104 if ((errno != EAGAIN) || (++failCt > 10))
1105 exit (EXIT_FAILURE);
1106 sleep (1);
1107 }
1108
b35926b9
BK
1109 /* IF we allocated a shell script command,
1110 THEN free it and restore the command format to the fix description */
bb786201
BK
1111 if (pz_cmd != (char*)NULL)
1112 {
1113 free ((void*)pz_cmd);
1114 p_fixd->patch_args[2] = pz_cmd_save;
1115 }
1116
1117 return read_fd;
1118}
62a99405 1119#endif
11c3a68e
BK
1120#ifdef DEBUG
1121# define NOTE_SKIP(_ttyp) do { \
1122 if (VLEVEL( VERB_EVERYTHING )) \
1123 fprintf (stderr, z_failed, _ttyp, p_fixd->fix_name, \
1124 pz_fname, p_fixd->test_ct - test_ct); \
1125 } while (0)
1126#else
1127# define NOTE_SKIP(_ttyp)
1128#endif
5abc1f74 1129
1f414ac4 1130/* * * * * * * * * * * * *
401be4b6
BK
1131 *
1132 * Process the potential fixes for a particular include file.
1133 * Input: the original text of the file and the file's name
1134 * Result: none. A new file may or may not be created.
1135 */
6864a6c6 1136static t_bool
f4dbf936 1137fix_applies (tFixDesc* p_fixd)
0083c904 1138{
ae5c839b
ZW
1139 const char *pz_fname = pz_curr_file;
1140 const char *pz_scan = p_fixd->file_list;
5abc1f74
BK
1141 int test_ct;
1142 tTestDesc *p_test;
11c3a68e
BK
1143 t_bool saw_sum_test = BOOL_FALSE;
1144 t_bool one_sum_passed = BOOL_FALSE;
0083c904 1145
716028e4 1146#if defined(__MSDOS__) && !defined(__DJGPP__)
62a99405
BK
1147 /*
1148 * There is only one fix that uses a shell script as of this writing.
1149 * I hope to nuke it anyway, it does not apply to DOS and it would
1150 * be painful to implement. Therefore, no "shell" fixes for DOS.
1151 */
1152 if (p_fixd->fd_flags & (FD_SHELL_SCRIPT | FD_SKIP_TEST))
1153 return BOOL_FALSE;
401be4b6 1154#else
5abc1f74
BK
1155 if (p_fixd->fd_flags & FD_SKIP_TEST)
1156 return BOOL_FALSE;
401be4b6 1157#endif
0083c904 1158
5abc1f74
BK
1159 /* IF there is a file name restriction,
1160 THEN ensure the current file name matches one in the pattern */
1161
ae5c839b 1162 if (pz_scan != (char *) NULL)
5abc1f74 1163 {
5abc1f74
BK
1164 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
1165 pz_fname += 2;
1f414ac4 1166
5abc1f74 1167 for (;;)
0083c904 1168 {
401be4b6 1169 if (fnmatch (pz_scan, pz_fname, 0) == 0)
5abc1f74 1170 break;
401be4b6
BK
1171 pz_scan += strlen (pz_scan) + 1;
1172 if (*pz_scan == NUL)
1173 return BOOL_FALSE;
5abc1f74
BK
1174 }
1175 }
0083c904 1176
5abc1f74 1177 /* FOR each test, see if it fails.
11c3a68e 1178 "sum" fails only if all "sum" tests fail.
5abc1f74 1179 IF it does fail, then we go on to the next test */
1f414ac4 1180
5abc1f74
BK
1181 for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct;
1182 test_ct-- > 0;
1183 p_test++)
1184 {
1185 switch (p_test->type)
1186 {
1187 case TT_TEST:
b35926b9 1188 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
11c3a68e 1189 NOTE_SKIP("TEST");
5abc1f74 1190 return BOOL_FALSE;
b35926b9 1191 }
5abc1f74
BK
1192 break;
1193
1194 case TT_EGREP:
b35926b9 1195 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
11c3a68e 1196 NOTE_SKIP("EGREP");
5abc1f74 1197 return BOOL_FALSE;
b35926b9 1198 }
5abc1f74
BK
1199 break;
1200
1201 case TT_NEGREP:
b35926b9 1202 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
11c3a68e 1203 NOTE_SKIP("NEGREP");
5abc1f74
BK
1204 /* Negated sense */
1205 return BOOL_FALSE;
b35926b9 1206 }
5abc1f74
BK
1207 break;
1208
11c3a68e
BK
1209 case TT_CKSUM:
1210 if (one_sum_passed)
efdfdb24 1211 break; /* No need to check any more */
11c3a68e
BK
1212
1213 saw_sum_test = BOOL_TRUE;
1214 if (cksum_test (pz_curr_data, p_test, pz_curr_file) != APPLY_FIX) {
1215 NOTE_SKIP("CKSUM");
1216 } else {
1217 one_sum_passed = BOOL_TRUE;
1218 }
1219 break;
1220
5abc1f74
BK
1221 case TT_FUNCTION:
1222 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
b35926b9 1223 != APPLY_FIX) {
11c3a68e 1224 NOTE_SKIP("FTEST");
5abc1f74 1225 return BOOL_FALSE;
b35926b9 1226 }
5abc1f74 1227 break;
0083c904 1228 }
5abc1f74 1229 }
0083c904 1230
11c3a68e
BK
1231 if (saw_sum_test)
1232 return one_sum_passed;
1233
5abc1f74
BK
1234 return BOOL_TRUE;
1235}
0083c904 1236
0083c904 1237
5abc1f74
BK
1238/* * * * * * * * * * * * *
1239
1240 Write out a replacement file */
1241
6864a6c6 1242static void
f4dbf936 1243write_replacement (tFixDesc* p_fixd)
5abc1f74
BK
1244{
1245 const char* pz_text = p_fixd->patch_args[0];
1246
1247 if ((pz_text == (char*)NULL) || (*pz_text == NUL))
1248 return;
1249
1250 {
6864a6c6 1251 FILE* out_fp = create_file ();
66f788b0
BK
1252 size_t sz = strlen (pz_text);
1253 fwrite (pz_text, sz, 1, out_fp);
1254 if (pz_text[ sz-1 ] != '\n')
1255 fputc ('\n', out_fp);
5abc1f74
BK
1256 fclose (out_fp);
1257 }
1258}
1259
1260
1261/* * * * * * * * * * * * *
1262
1263 We have work to do. Read back in the output
1264 of the filtering chain. Compare each byte as we read it with
1265 the contents of the original file. As soon as we find any
1266 difference, we will create the output file, write out all
1267 the matched text and then copy any remaining data from the
1268 output of the filter chain.
1269 */
6864a6c6 1270static void
f4dbf936 1271test_for_changes (int read_fd)
5abc1f74
BK
1272{
1273 FILE *in_fp = fdopen (read_fd, "r");
1274 FILE *out_fp = (FILE *) NULL;
90376ae2 1275 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
5abc1f74
BK
1276
1277#ifdef DO_STATS
1278 fixed_ct++;
1f414ac4 1279#endif
5abc1f74
BK
1280 for (;;)
1281 {
1282 int ch;
0083c904 1283
5abc1f74
BK
1284 ch = getc (in_fp);
1285 if (ch == EOF)
1286 break;
90376ae2 1287 ch &= 0xFF; /* all bytes are 8 bits */
5abc1f74
BK
1288
1289 /* IF we are emitting the output
1290 THEN emit this character, too.
1291 */
1292 if (out_fp != (FILE *) NULL)
1293 putc (ch, out_fp);
1294
1295 /* ELSE if this character does not match the original,
1296 THEN now is the time to start the output.
1297 */
1298 else if (ch != *pz_cmp)
1299 {
6864a6c6 1300 out_fp = create_file ();
5abc1f74
BK
1301
1302#ifdef DO_STATS
1303 altered_ct++;
1f414ac4 1304#endif
5abc1f74 1305 /* IF there are matched data, write the matched part now. */
c8b0c191
BK
1306 if ((char*)pz_cmp != pz_curr_data)
1307 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
11c3a68e 1308 1, out_fp);
5abc1f74
BK
1309
1310 /* Emit the current unmatching character */
1311 putc (ch, out_fp);
0083c904 1312 }
5abc1f74
BK
1313 else
1314 /* ELSE the character matches. Advance the compare ptr */
1315 pz_cmp++;
1316 }
1317
1318 /* IF we created the output file, ... */
1319 if (out_fp != (FILE *) NULL)
1320 {
1321 regmatch_t match;
1322
1323 /* Close the file and see if we have to worry about
1324 `#include "file.h"' constructs. */
1325 fclose (out_fp);
ab747408 1326 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
5abc1f74
BK
1327 extract_quoted_files (pz_curr_data, pz_curr_file, &match);
1328 }
1329
1330 fclose (in_fp);
1331 close (read_fd); /* probably redundant, but I'm paranoid */
1332}
1333
1334
1335/* * * * * * * * * * * * *
1336
1337 Process the potential fixes for a particular include file.
1338 Input: the original text of the file and the file's name
1339 Result: none. A new file may or may not be created. */
1340
1341void
f4dbf936 1342process (void)
5abc1f74 1343{
5abc1f74
BK
1344 tFixDesc *p_fixd = fixDescList;
1345 int todo_ct = FIX_COUNT;
1346 int read_fd = -1;
283da1d3 1347# ifndef SEPARATE_FIX_PROC
5abc1f74 1348 int num_children = 0;
283da1d3 1349# else /* is SEPARATE_FIX_PROC */
62a99405
BK
1350 char* pz_file_source = pz_curr_file;
1351# endif
5abc1f74
BK
1352
1353 if (access (pz_curr_file, R_OK) != 0)
1354 {
3363022e
XR
1355 /* It may happens if for e. g. the distro ships some broken symlinks
1356 in /usr/include. */
1357
1358 /* "INPUT" is exported in fixinc.sh, which is the pwd where fixincl
1359 runs. It's used instead of getcwd to avoid allocating a buffer
1360 with unknown length. */
1361 const char *cwd = getenv ("INPUT");
1362 if (!cwd)
1363 cwd = "the working directory";
1364
1365 fprintf (stderr, "Cannot access %s from %s: %s\n", pz_curr_file, cwd,
04c5a91d 1366 xstrerror (errno));
3363022e 1367 return;
5abc1f74
BK
1368 }
1369
1370 pz_curr_data = load_file (pz_curr_file);
1371 if (pz_curr_data == (char *) NULL)
1372 return;
1373
1374#ifdef DO_STATS
1375 process_ct++;
1376#endif
d7eb5a45 1377 if (VLEVEL( VERB_PROGRESS ) && have_tty)
1c99d804 1378 fprintf (stderr, "%6lu %-50s \r",
11c3a68e 1379 (unsigned long) data_map_size, pz_curr_file);
5abc1f74 1380
283da1d3 1381# ifndef SEPARATE_FIX_PROC
5abc1f74
BK
1382 process_chain_head = NOPROCESS;
1383
1384 /* For every fix in our fix list, ... */
1385 for (; todo_ct > 0; p_fixd++, todo_ct--)
1386 {
1387 if (! fix_applies (p_fixd))
1388 continue;
0083c904 1389
b35926b9
BK
1390 if (VLEVEL( VERB_APPLIES ))
1391 fprintf (stderr, "Applying %-24s to %s\n",
1392 p_fixd->fix_name, pz_curr_file);
5abc1f74
BK
1393
1394 if (p_fixd->fd_flags & FD_REPLACEMENT)
1395 {
1396 write_replacement (p_fixd);
1397 UNLOAD_DATA();
1398 return;
1399 }
1f414ac4
BK
1400
1401 /* IF we do not have a read pointer,
1402 THEN this is the first fix for the current file.
1403 Open the source file. That will be used as stdin for
1404 the first fix. Any subsequent fixes will use the
5abc1f74 1405 stdout descriptor of the previous fix for its stdin. */
0083c904 1406
48ac9ce2 1407 if (read_fd == -1)
1f414ac4 1408 {
5abc1f74 1409 read_fd = open (pz_curr_file, O_RDONLY);
48ac9ce2 1410 if (read_fd < 0)
0083c904 1411 {
1f414ac4 1412 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
f95e46b9 1413 xstrerror (errno), pz_curr_file);
1f414ac4 1414 exit (EXIT_FAILURE);
0083c904 1415 }
5abc1f74
BK
1416
1417 /* Ensure we do not get duplicate output */
1418
1419 fflush (stdout);
1f414ac4 1420 }
0083c904 1421
5abc1f74 1422 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
bb786201 1423 num_children++;
0083c904
BK
1424 }
1425
5abc1f74
BK
1426 /* IF we have a read-back file descriptor,
1427 THEN check for changes and write output if changed. */
1f414ac4 1428
5abc1f74
BK
1429 if (read_fd >= 0)
1430 {
1431 test_for_changes (read_fd);
1432#ifdef DO_STATS
1433 apply_ct += num_children;
1434#endif
1435 /* Wait for child processes created by chain_open()
1436 to avoid leaving zombies. */
1437 do {
1438 wait ((int *) NULL);
1439 } while (--num_children > 0);
1440 }
9f8eec39 1441
283da1d3 1442# else /* is SEPARATE_FIX_PROC */
62a99405
BK
1443
1444 for (; todo_ct > 0; p_fixd++, todo_ct--)
1445 {
1446 if (! fix_applies (p_fixd))
1447 continue;
1448
1449 if (VLEVEL( VERB_APPLIES ))
1450 fprintf (stderr, "Applying %-24s to %s\n",
1451 p_fixd->fix_name, pz_curr_file);
1452
1453 if (p_fixd->fd_flags & FD_REPLACEMENT)
1454 {
1455 write_replacement (p_fixd);
1456 UNLOAD_DATA();
1457 return;
1458 }
1459 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
1460 pz_file_source = pz_temp_file;
1461 }
1462
283da1d3
DB
1463 read_fd = open (pz_temp_file, O_RDONLY);
1464 if (read_fd < 0)
1465 {
ca21b4a5
BK
1466 if (errno != ENOENT)
1467 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
1468 errno, xstrerror (errno), pz_temp_file);
283da1d3
DB
1469 }
1470 else
1471 {
1472 test_for_changes (read_fd);
1473 /* Unlinking a file while it is still open is a Bad Idea on
1474 DOS/Windows. */
1475 close (read_fd);
1476 unlink (pz_temp_file);
1477 }
62a99405
BK
1478
1479# endif
5abc1f74 1480 UNLOAD_DATA();
0083c904 1481}