0
|
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
|
|
3 directory that GCC will search.
|
|
4
|
111
|
5 Copyright (C) 1997, 1998, 1999, 2000, 2004, 2009, 2012
|
0
|
6 Free Software Foundation, Inc.
|
|
7
|
|
8 This file is part of GCC.
|
|
9
|
|
10 GCC is free software; you can redistribute it and/or modify
|
|
11 it under the terms of the GNU General Public License as published by
|
|
12 the Free Software Foundation; either version 3, or (at your option)
|
|
13 any later version.
|
|
14
|
|
15 GCC is distributed in the hope that it will be useful,
|
|
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18 GNU General Public License for more details.
|
|
19
|
|
20 You should have received a copy of the GNU General Public License
|
|
21 along with GCC; see the file COPYING3. If not see
|
|
22 <http://www.gnu.org/licenses/>. */
|
|
23
|
|
24 #include "fixlib.h"
|
|
25
|
|
26 #include <fnmatch.h>
|
|
27 #include <sys/stat.h>
|
|
28 #ifndef SEPARATE_FIX_PROC
|
|
29 #include <sys/wait.h>
|
|
30 #endif
|
|
31
|
|
32 #if defined( HAVE_MMAP_FILE )
|
|
33 #include <sys/mman.h>
|
|
34 #define BAD_ADDR ((void*)-1)
|
|
35 #endif
|
|
36
|
|
37 #ifndef SEPARATE_FIX_PROC
|
|
38 #include "server.h"
|
|
39 #endif
|
|
40
|
|
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. */
|
|
43
|
|
44 static const char program_id[] = "fixincl version 1.1";
|
|
45
|
|
46 /* This format will be used at the start of every generated file */
|
|
47
|
|
48 static 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
|
|
55 int find_base_len = 0;
|
|
56 int have_tty = 0;
|
|
57
|
|
58 pid_t process_chain_head = (pid_t) -1;
|
|
59
|
|
60 char* pz_curr_file; /* name of the current file under test/fix */
|
|
61 char* pz_curr_data; /* original contents of that file */
|
|
62 char* pz_temp_file; /* for DOS, a place to stash the temporary
|
|
63 fixed data between system(3) calls */
|
|
64 t_bool curr_data_mapped;
|
|
65 int data_map_fd;
|
|
66 size_t data_map_size;
|
|
67 size_t ttl_data_size = 0;
|
|
68
|
|
69 #ifdef DO_STATS
|
|
70 int process_ct = 0;
|
|
71 int apply_ct = 0;
|
|
72 int fixed_ct = 0;
|
|
73 int altered_ct = 0;
|
|
74 #endif /* DO_STATS */
|
|
75
|
|
76 const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]";
|
111
|
77 regex_t incl_quote_re;
|
|
78
|
|
79 #ifndef SEPARATE_FIX_PROC
|
0
|
80 tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n";
|
111
|
81 #endif
|
0
|
82
|
|
83 static void do_version (void) ATTRIBUTE_NORETURN;
|
|
84 char *load_file (const char *);
|
|
85 void run_compiles (void);
|
|
86 void initialize (int argc, char** argv);
|
|
87 void process (void);
|
|
88
|
|
89 /* External Source Code */
|
|
90
|
|
91 #include "fixincl.x"
|
|
92
|
|
93 /* * * * * * * * * * * * * * * * * * *
|
|
94 *
|
|
95 * MAIN ROUTINE
|
|
96 */
|
|
97 extern int main (int, char **);
|
|
98 int
|
|
99 main (int argc, char** argv)
|
|
100 {
|
|
101 char *file_name_buf;
|
|
102
|
|
103 initialize ( argc, argv );
|
|
104
|
|
105 have_tty = isatty (fileno (stderr));
|
|
106
|
|
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
|
|
122 for (;;)
|
|
123 {
|
|
124 char* pz_end;
|
|
125
|
|
126 /* skip to start of name, past any "./" prefixes */
|
|
127
|
|
128 while (ISSPACE (*file_name_buf)) file_name_buf++;
|
|
129 while ((file_name_buf[0] == '.') && (file_name_buf[1] == '/'))
|
|
130 file_name_buf += 2;
|
|
131
|
|
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;
|
|
153
|
|
154 process ();
|
|
155 } /* for (;;) */
|
|
156
|
|
157 #ifdef DO_STATS
|
|
158 if (VLEVEL( VERB_PROGRESS )) {
|
|
159 tSCC zFmt[] =
|
|
160 "\
|
|
161 Processed %5d files containing %d bytes \n\
|
|
162 Applying %5d fixes to %d files\n\
|
|
163 Altering %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 */
|
|
169
|
|
170 # ifdef SEPARATE_FIX_PROC
|
|
171 unlink( pz_temp_file );
|
|
172 # endif
|
|
173 exit (EXIT_SUCCESS);
|
|
174 }
|
|
175
|
|
176
|
|
177 static void
|
|
178 do_version (void)
|
|
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)
|
|
185 2. that we can compile all the regular expressions.
|
|
186 3. we can correctly run our server shell process
|
|
187 */
|
|
188 run_compiles ();
|
|
189 sprintf (zBuf, zFmt, program_id);
|
|
190 #ifndef SEPARATE_FIX_PROC
|
|
191 puts (zBuf + 5);
|
|
192 exit (strcmp (run_shell (zBuf), program_id));
|
|
193 #else
|
111
|
194 exit (system_with_shell (zBuf));
|
0
|
195 #endif
|
|
196 }
|
|
197
|
|
198 /* * * * * * * * * * * * */
|
|
199
|
|
200 void
|
|
201 initialize ( int argc, char** argv )
|
|
202 {
|
|
203 xmalloc_set_program_name (argv[0]);
|
|
204
|
|
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
|
|
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
|
|
232 initialize_opts ();
|
|
233
|
|
234 if (ISDIGIT ( *pz_verbose ))
|
|
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
|
|
250 default:
|
|
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 }
|
|
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 );
|
|
271
|
|
272 /* Compile all the regular expressions now.
|
|
273 That way, it is done only once for the whole run.
|
|
274 */
|
|
275 run_compiles ();
|
|
276
|
|
277 # ifdef SEPARATE_FIX_PROC
|
|
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" );
|
111
|
281
|
|
282 #if defined(__MINGW32__)
|
|
283 fix_path_separators (pz_temp_file);
|
|
284 #endif
|
|
285
|
0
|
286 # endif
|
|
287
|
|
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);
|
|
293 }
|
|
294
|
|
295 /* * * * * * * * * * * * *
|
|
296
|
|
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. */
|
|
301 char *
|
|
302 load_file ( const char* fname )
|
|
303 {
|
|
304 struct stat stbf;
|
|
305 char* res;
|
|
306
|
|
307 if (stat (fname, &stbf) != 0)
|
|
308 {
|
|
309 if (NOT_SILENT)
|
|
310 fprintf (stderr, "error %d (%s) stat-ing %s\n",
|
|
311 errno, xstrerror (errno), fname );
|
|
312 return (char *) NULL;
|
|
313 }
|
|
314 if (stbf.st_size == 0)
|
|
315 return (char*)NULL;
|
|
316
|
|
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. */
|
|
321 data_map_size = stbf.st_size+1;
|
|
322 data_map_fd = open (fname, O_RDONLY);
|
|
323 ttl_data_size += data_map_size-1;
|
|
324
|
|
325 if (data_map_fd < 0)
|
|
326 {
|
|
327 if (NOT_SILENT)
|
|
328 fprintf (stderr, "error %d (%s) opening %s for read\n",
|
|
329 errno, xstrerror (errno), fname);
|
|
330 return (char*)NULL;
|
|
331 }
|
|
332
|
|
333 #ifdef HAVE_MMAP_FILE
|
|
334 curr_data_mapped = BOOL_TRUE;
|
|
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 */
|
|
338 if ((stbf.st_size & (getpagesize()-1)) == 0)
|
|
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);
|
|
343 if (res == (char*)BAD_ADDR)
|
|
344 #endif
|
|
345 {
|
|
346 FILE* fp = fdopen (data_map_fd, "r");
|
|
347 curr_data_mapped = BOOL_FALSE;
|
|
348 res = load_file_data (fp);
|
|
349 fclose (fp);
|
|
350 }
|
|
351
|
|
352 return res;
|
|
353 }
|
|
354
|
|
355 static int
|
|
356 machine_matches( tFixDesc* p_fixd )
|
|
357 {
|
|
358 char const ** papz_machs = p_fixd->papz_machs;
|
|
359 int have_match = BOOL_FALSE;
|
|
360
|
|
361 for (;;)
|
|
362 {
|
|
363 char const * pz_mpat = *(papz_machs++);
|
|
364 if (pz_mpat == NULL)
|
|
365 break;
|
|
366 if (fnmatch(pz_mpat, pz_machine, 0) == 0)
|
|
367 {
|
|
368 have_match = BOOL_TRUE;
|
|
369 break;
|
|
370 }
|
|
371 }
|
|
372
|
|
373 /* Check for sense inversion then set the "skip test" flag, if needed */
|
|
374 if (p_fixd->fd_flags & FD_MACH_IFNOT)
|
|
375 have_match = ! have_match;
|
|
376
|
|
377 if (! have_match)
|
|
378 p_fixd->fd_flags |= FD_SKIP_TEST;
|
|
379
|
|
380 return have_match;
|
|
381 }
|
|
382
|
|
383 /* * * * * * * * * * * * *
|
|
384 *
|
|
385 * run_compiles run all the regexp compiles for all the fixes once.
|
|
386 */
|
|
387 void
|
|
388 run_compiles (void)
|
|
389 {
|
|
390 tFixDesc *p_fixd = fixDescList;
|
|
391 int fix_ct = FIX_COUNT;
|
|
392 regex_t *p_re = XCNEWVEC (regex_t, REGEX_COUNT);
|
|
393
|
|
394 /* Make sure compile_re does not stumble across invalid data */
|
|
395
|
|
396 memset (&incl_quote_re, '\0', sizeof (regex_t));
|
|
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 {
|
111
|
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;
|
0
|
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;
|
|
429
|
|
430 /* FOR every test for the fixup, ... */
|
|
431
|
|
432 while (--test_ct >= 0)
|
|
433 {
|
|
434 switch (p_test->type)
|
|
435 {
|
|
436 case TT_EGREP:
|
|
437 case TT_NEGREP:
|
|
438 p_test->p_test_regex = p_re++;
|
|
439 compile_re (p_test->pz_test_text, p_test->p_test_regex, 0,
|
|
440 "select test", p_fixd->fix_name);
|
|
441 default: break;
|
|
442 }
|
|
443 p_test++;
|
|
444 }
|
|
445 }
|
|
446 while (p_fixd++, --fix_ct > 0);
|
|
447 }
|
|
448
|
|
449
|
|
450 /* * * * * * * * * * * * *
|
|
451
|
|
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
|
|
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
|
|
472
|
|
473 static FILE *
|
|
474 create_file (void)
|
|
475 {
|
|
476 int fd;
|
|
477 FILE *pf;
|
|
478 char fname[MAXPATHLEN];
|
|
479
|
|
480 sprintf (fname, "%s/%s", pz_dest_dir, pz_curr_file + find_base_len);
|
|
481
|
|
482 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
|
|
483
|
|
484 /* We may need to create the directories needed... */
|
|
485 if ((fd < 0) && (errno == ENOENT))
|
|
486 {
|
|
487 char *pz_dir = strchr (fname + 1, '/');
|
|
488 struct stat stbf;
|
|
489
|
|
490 while (pz_dir != (char *) NULL)
|
|
491 {
|
|
492 *pz_dir = NUL;
|
|
493 if (stat (fname, &stbf) < 0)
|
|
494 {
|
|
495 #ifdef _WIN32
|
|
496 mkdir (fname);
|
|
497 #else
|
|
498 mkdir (fname, S_IFDIR | S_DIRALL);
|
|
499 #endif
|
|
500 }
|
|
501
|
|
502 *pz_dir = '/';
|
|
503 pz_dir = strchr (pz_dir + 1, '/');
|
|
504 }
|
|
505
|
|
506 /* Now, lets try the open again... */
|
|
507 fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL);
|
|
508 }
|
|
509 if (fd < 0)
|
|
510 {
|
|
511 fprintf (stderr, "Error %d (%s) creating %s\n",
|
|
512 errno, xstrerror (errno), fname);
|
|
513 exit (EXIT_FAILURE);
|
|
514 }
|
|
515 if (NOT_SILENT)
|
|
516 fprintf (stderr, "Fixed: %s\n", pz_curr_file);
|
|
517 pf = fdopen (fd, "w");
|
|
518
|
|
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);
|
|
528
|
|
529 return pf;
|
|
530 }
|
|
531
|
|
532
|
|
533 /* * * * * * * * * * * * *
|
|
534
|
|
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
|
|
538 Result: APPLY_FIX or SKIP_FIX, depending on the result of the
|
|
539 shell script we run. */
|
|
540 #ifndef SEPARATE_FIX_PROC
|
|
541 static int
|
|
542 test_test (tTestDesc* p_test, char* pz_test_file)
|
|
543 {
|
|
544 tSCC cmd_fmt[] =
|
|
545 "file=%s\n\
|
|
546 if ( test %s ) > /dev/null 2>&1\n\
|
|
547 then echo TRUE\n\
|
|
548 else echo FALSE\n\
|
|
549 fi";
|
|
550
|
|
551 char *pz_res;
|
|
552 int res;
|
|
553
|
|
554 static char cmd_buf[4096];
|
|
555
|
|
556 sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text);
|
|
557 pz_res = run_shell (cmd_buf);
|
|
558
|
|
559 switch (*pz_res) {
|
|
560 case 'T':
|
|
561 res = APPLY_FIX;
|
|
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 );
|
|
571 res = SKIP_FIX;
|
|
572 }
|
|
573
|
|
574 free ((void *) pz_res);
|
|
575 return res;
|
|
576 }
|
111
|
577 #elif defined(__MINGW32__) || defined(__DJGPP__)
|
|
578 static int
|
|
579 test_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 }
|
0
|
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
|
|
604
|
|
605 /* * * * * * * * * * * * *
|
|
606
|
|
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
|
|
610 Result: APPLY_FIX if the pattern is found, SKIP_FIX otherwise
|
|
611
|
|
612 The caller may choose to reverse meaning if the sense of the test
|
|
613 is inverted. */
|
|
614
|
|
615 static int
|
|
616 egrep_test (char* pz_data, tTestDesc* p_test)
|
|
617 {
|
|
618 #ifdef DEBUG
|
|
619 if (p_test->p_test_regex == 0)
|
|
620 fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n",
|
|
621 p_test->pz_test_text);
|
|
622 #endif
|
|
623 if (xregexec (p_test->p_test_regex, pz_data, 0, 0, 0) == 0)
|
|
624 return APPLY_FIX;
|
|
625 return SKIP_FIX;
|
|
626 }
|
|
627
|
111
|
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
|
|
637 static int
|
|
638 cksum_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 }
|
0
|
685
|
|
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
|
|
692 static int
|
|
693 quoted_file_exists (const char* pz_src_path,
|
|
694 const char* pz_file_path,
|
|
695 const char* pz_file)
|
|
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++;
|
|
704 if (! ISGRAPH( ch ))
|
|
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
|
|
720 /* * * * * * * * * * * * *
|
|
721 *
|
|
722 extract_quoted_files
|
|
723
|
|
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:
|
|
731
|
|
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 */
|
|
740
|
|
741
|
|
742 static void
|
|
743 extract_quoted_files (char* pz_data,
|
|
744 const char* pz_fixed_file,
|
|
745 regmatch_t* p_re_match)
|
|
746 {
|
|
747 char *pz_dir_end = strrchr (pz_fixed_file, '/');
|
|
748 char *pz_incl_quot = pz_data;
|
|
749
|
|
750 if (VLEVEL( VERB_APPLIES ))
|
|
751 fprintf (stderr, "Quoted includes in %s\n", pz_fixed_file);
|
|
752
|
|
753 /* Set "pz_fixed_file" to point to the containing subdirectory of the source
|
|
754 If there is none, then it is in our current directory, ".". */
|
|
755
|
|
756 if (pz_dir_end == (char *) NULL)
|
|
757 pz_fixed_file = ".";
|
|
758 else
|
|
759 *pz_dir_end = '\0';
|
|
760
|
|
761 for (;;)
|
|
762 {
|
|
763 pz_incl_quot += p_re_match->rm_so;
|
|
764
|
|
765 /* Skip forward to the included file name */
|
|
766 while (*pz_incl_quot != '"')
|
|
767 pz_incl_quot++;
|
|
768
|
|
769 if (quoted_file_exists (pz_src_dir, pz_fixed_file, pz_incl_quot))
|
|
770 {
|
|
771 /* Print the source directory and the subdirectory
|
|
772 of the file in question. */
|
|
773 printf ("%s %s/", pz_src_dir, pz_fixed_file);
|
|
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 */
|
|
782 printf (" %s/%s/", pz_dest_dir, pz_fixed_file);
|
|
783 while (*pz_dir_end != '"')
|
|
784 putc (*pz_dir_end++, stdout);
|
|
785
|
|
786 /* End of entry */
|
|
787 putc ('\n', stdout);
|
|
788 }
|
|
789
|
|
790 /* Find the next entry */
|
|
791 if (xregexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0)
|
|
792 break;
|
|
793 }
|
|
794 }
|
|
795
|
|
796
|
|
797 /* * * * * * * * * * * * *
|
|
798
|
|
799 Somebody wrote a *_fix subroutine that we must call.
|
|
800 */
|
|
801 #ifndef SEPARATE_FIX_PROC
|
|
802 static int
|
|
803 internal_fix (int read_fd, tFixDesc* p_fixd)
|
|
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 */
|
|
838 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
|
|
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 */
|
|
859 dup2 (fd[1], STDOUT_FILENO);
|
|
860 dup2 (read_fd, STDIN_FILENO);
|
|
861
|
|
862 apply_fix (p_fixd, pz_curr_file);
|
|
863 exit (0);
|
|
864 }
|
|
865 #endif /* !SEPARATE_FIX_PROC */
|
|
866
|
|
867
|
|
868 #ifdef SEPARATE_FIX_PROC
|
|
869 static void
|
|
870 fix_with_system (tFixDesc* p_fixd,
|
|
871 tCC* pz_fix_file,
|
|
872 tCC* pz_file_source,
|
|
873 tCC* pz_temp_file)
|
|
874 {
|
|
875 char* pz_cmd;
|
|
876 char* pz_scan;
|
|
877 size_t argsize;
|
|
878
|
|
879 if (p_fixd->fd_flags & FD_SUBROUTINE)
|
|
880 {
|
|
881 static const char z_applyfix_prog[] =
|
111
|
882 "/../fixincludes/applyfix" EXE_EXT;
|
0
|
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 */
|
|
893 pz_cmd = XNEWVEC (char, argsize);
|
|
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,
|
111
|
900 try one level higher up */
|
0
|
901 if (stat (pz_cmd, &buf) == -1)
|
111
|
902 {
|
|
903 strcpy (pz_scan, "/..");
|
|
904 strcpy (pz_scan+3, z_applyfix_prog);
|
|
905 }
|
0
|
906
|
|
907 pz_scan += strlen (pz_scan);
|
|
908
|
|
909 /*
|
|
910 * Now add the fix number and file names that may be needed
|
|
911 */
|
111
|
912 sprintf (pz_scan, " %ld '%s' '%s' '%s'", (long) (p_fixd - fixDescList),
|
|
913 pz_fix_file, pz_file_source, pz_temp_file);
|
0
|
914 }
|
|
915 else /* NOT an "internal" fix: */
|
|
916 {
|
|
917 size_t parg_size;
|
111
|
918 #if defined(__MSDOS__) && !defined(__DJGPP__)
|
0
|
919 /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick:
|
|
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
|
|
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.
|
|
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
|
|
930 'cat' to read (pipes do not work on DOS). */
|
|
931 tSCC z_cmd_fmt[] = " '%s' | cat > '%s'";
|
|
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
|
111
|
937 tSCC z_subshell_start[] = "( ";
|
|
938 tSCC z_subshell_end[] = " ) < ";
|
0
|
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
|
111
|
945 if (p_fixd->fd_flags & FD_SHELL_SCRIPT)
|
|
946 {
|
|
947 argsize += strlen( z_subshell_start ) + strlen ( z_subshell_end );
|
|
948 }
|
0
|
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. */
|
|
966 pz_scan = pz_cmd = XNEWVEC (char, argsize);
|
|
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
|
|
973 /*
|
111
|
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
|
|
983 /*
|
0
|
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++);
|
111
|
1003 char* pz_scan_save;
|
0
|
1004 if (pArg == NULL)
|
|
1005 break;
|
|
1006 *(pz_scan++) = ' ';
|
|
1007 pz_scan = make_raw_shell_str( pz_scan_save = pz_scan, pArg,
|
111
|
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 }
|
|
1022 }
|
|
1023
|
|
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);
|
0
|
1031 }
|
|
1032
|
|
1033 /*
|
|
1034 * add the file machinations.
|
|
1035 */
|
111
|
1036 #if defined(__MSDOS__) && !defined(__DJGPP__)
|
0
|
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
|
|
1042 }
|
111
|
1043 system_with_shell (pz_cmd);
|
|
1044 free (pz_cmd);
|
0
|
1045 }
|
|
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
|
|
1054 #else /* is *NOT* SEPARATE_FIX_PROC */
|
|
1055 static int
|
|
1056 start_fixer (int read_fd, tFixDesc* p_fixd, char* pz_fix_file)
|
|
1057 {
|
|
1058 tCC* pz_cmd_save;
|
|
1059 char* pz_cmd;
|
|
1060
|
|
1061 if ((p_fixd->fd_flags & FD_SUBROUTINE) != 0)
|
|
1062 return internal_fix (read_fd, p_fixd);
|
|
1063
|
|
1064 if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0)
|
|
1065 {
|
|
1066 pz_cmd = NULL;
|
|
1067 pz_cmd_save = NULL;
|
|
1068 }
|
|
1069 else
|
|
1070 {
|
|
1071 tSCC z_cmd_fmt[] = "file='%s'\n%s";
|
|
1072 pz_cmd = XNEWVEC (char, strlen (p_fixd->patch_args[2])
|
111
|
1073 + sizeof (z_cmd_fmt) + strlen (pz_fix_file));
|
0
|
1074 sprintf (pz_cmd, z_cmd_fmt, pz_fix_file, p_fixd->patch_args[2]);
|
|
1075 pz_cmd_save = p_fixd->patch_args[2];
|
|
1076 p_fixd->patch_args[2] = pz_cmd;
|
|
1077 }
|
|
1078
|
|
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 */
|
|
1085 for (;;)
|
|
1086 {
|
|
1087 static int failCt = 0;
|
|
1088 int fd;
|
|
1089
|
|
1090 fd = chain_open (read_fd,
|
|
1091 (tCC **) p_fixd->patch_args,
|
|
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
|
|
1101 fprintf (stderr, z_fork_err, errno, xstrerror (errno),
|
|
1102 p_fixd->fix_name);
|
|
1103
|
|
1104 if ((errno != EAGAIN) || (++failCt > 10))
|
|
1105 exit (EXIT_FAILURE);
|
|
1106 sleep (1);
|
|
1107 }
|
|
1108
|
|
1109 /* IF we allocated a shell script command,
|
|
1110 THEN free it and restore the command format to the fix description */
|
|
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 }
|
|
1119 #endif
|
111
|
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
|
0
|
1129
|
|
1130 /* * * * * * * * * * * * *
|
|
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 */
|
|
1136 static t_bool
|
|
1137 fix_applies (tFixDesc* p_fixd)
|
|
1138 {
|
|
1139 const char *pz_fname = pz_curr_file;
|
|
1140 const char *pz_scan = p_fixd->file_list;
|
|
1141 int test_ct;
|
|
1142 tTestDesc *p_test;
|
111
|
1143 t_bool saw_sum_test = BOOL_FALSE;
|
|
1144 t_bool one_sum_passed = BOOL_FALSE;
|
0
|
1145
|
111
|
1146 #if defined(__MSDOS__) && !defined(__DJGPP__)
|
0
|
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;
|
|
1154 #else
|
|
1155 if (p_fixd->fd_flags & FD_SKIP_TEST)
|
|
1156 return BOOL_FALSE;
|
|
1157 #endif
|
|
1158
|
|
1159 /* IF there is a file name restriction,
|
|
1160 THEN ensure the current file name matches one in the pattern */
|
|
1161
|
|
1162 if (pz_scan != (char *) NULL)
|
|
1163 {
|
|
1164 while ((pz_fname[0] == '.') && (pz_fname[1] == '/'))
|
|
1165 pz_fname += 2;
|
|
1166
|
|
1167 for (;;)
|
|
1168 {
|
|
1169 if (fnmatch (pz_scan, pz_fname, 0) == 0)
|
|
1170 break;
|
|
1171 pz_scan += strlen (pz_scan) + 1;
|
|
1172 if (*pz_scan == NUL)
|
|
1173 return BOOL_FALSE;
|
|
1174 }
|
|
1175 }
|
|
1176
|
|
1177 /* FOR each test, see if it fails.
|
111
|
1178 "sum" fails only if all "sum" tests fail.
|
0
|
1179 IF it does fail, then we go on to the next test */
|
|
1180
|
|
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:
|
|
1188 if (test_test (p_test, pz_curr_file) != APPLY_FIX) {
|
111
|
1189 NOTE_SKIP("TEST");
|
0
|
1190 return BOOL_FALSE;
|
|
1191 }
|
|
1192 break;
|
|
1193
|
|
1194 case TT_EGREP:
|
|
1195 if (egrep_test (pz_curr_data, p_test) != APPLY_FIX) {
|
111
|
1196 NOTE_SKIP("EGREP");
|
0
|
1197 return BOOL_FALSE;
|
|
1198 }
|
|
1199 break;
|
|
1200
|
|
1201 case TT_NEGREP:
|
|
1202 if (egrep_test (pz_curr_data, p_test) == APPLY_FIX) {
|
111
|
1203 NOTE_SKIP("NEGREP");
|
0
|
1204 /* Negated sense */
|
|
1205 return BOOL_FALSE;
|
|
1206 }
|
|
1207 break;
|
|
1208
|
111
|
1209 case TT_CKSUM:
|
|
1210 if (one_sum_passed)
|
|
1211 break; /* No need to check any more */
|
|
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
|
0
|
1221 case TT_FUNCTION:
|
|
1222 if (run_test (p_test->pz_test_text, pz_curr_file, pz_curr_data)
|
|
1223 != APPLY_FIX) {
|
111
|
1224 NOTE_SKIP("FTEST");
|
0
|
1225 return BOOL_FALSE;
|
|
1226 }
|
|
1227 break;
|
|
1228 }
|
|
1229 }
|
|
1230
|
111
|
1231 if (saw_sum_test)
|
|
1232 return one_sum_passed;
|
|
1233
|
0
|
1234 return BOOL_TRUE;
|
|
1235 }
|
|
1236
|
|
1237
|
|
1238 /* * * * * * * * * * * * *
|
|
1239
|
|
1240 Write out a replacement file */
|
|
1241
|
|
1242 static void
|
|
1243 write_replacement (tFixDesc* p_fixd)
|
|
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 {
|
|
1251 FILE* out_fp = create_file ();
|
|
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);
|
|
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 */
|
|
1270 static void
|
|
1271 test_for_changes (int read_fd)
|
|
1272 {
|
|
1273 FILE *in_fp = fdopen (read_fd, "r");
|
|
1274 FILE *out_fp = (FILE *) NULL;
|
|
1275 unsigned char *pz_cmp = (unsigned char*)pz_curr_data;
|
|
1276
|
|
1277 #ifdef DO_STATS
|
|
1278 fixed_ct++;
|
|
1279 #endif
|
|
1280 for (;;)
|
|
1281 {
|
|
1282 int ch;
|
|
1283
|
|
1284 ch = getc (in_fp);
|
|
1285 if (ch == EOF)
|
|
1286 break;
|
|
1287 ch &= 0xFF; /* all bytes are 8 bits */
|
|
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 {
|
|
1300 out_fp = create_file ();
|
|
1301
|
|
1302 #ifdef DO_STATS
|
|
1303 altered_ct++;
|
|
1304 #endif
|
|
1305 /* IF there are matched data, write the matched part now. */
|
|
1306 if ((char*)pz_cmp != pz_curr_data)
|
|
1307 fwrite (pz_curr_data, (size_t)((char*)pz_cmp - pz_curr_data),
|
111
|
1308 1, out_fp);
|
0
|
1309
|
|
1310 /* Emit the current unmatching character */
|
|
1311 putc (ch, out_fp);
|
|
1312 }
|
|
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);
|
|
1326 if (xregexec (&incl_quote_re, pz_curr_data, 1, &match, 0) == 0)
|
|
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
|
|
1341 void
|
|
1342 process (void)
|
|
1343 {
|
|
1344 tFixDesc *p_fixd = fixDescList;
|
|
1345 int todo_ct = FIX_COUNT;
|
|
1346 int read_fd = -1;
|
|
1347 # ifndef SEPARATE_FIX_PROC
|
|
1348 int num_children = 0;
|
|
1349 # else /* is SEPARATE_FIX_PROC */
|
|
1350 char* pz_file_source = pz_curr_file;
|
|
1351 # endif
|
|
1352
|
|
1353 if (access (pz_curr_file, R_OK) != 0)
|
|
1354 {
|
|
1355 int erno = errno;
|
|
1356 fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n",
|
|
1357 pz_curr_file, getcwd ((char *) NULL, MAXPATHLEN),
|
|
1358 erno, xstrerror (erno));
|
|
1359 return;
|
|
1360 }
|
|
1361
|
|
1362 pz_curr_data = load_file (pz_curr_file);
|
|
1363 if (pz_curr_data == (char *) NULL)
|
|
1364 return;
|
|
1365
|
|
1366 #ifdef DO_STATS
|
|
1367 process_ct++;
|
|
1368 #endif
|
|
1369 if (VLEVEL( VERB_PROGRESS ) && have_tty)
|
|
1370 fprintf (stderr, "%6lu %-50s \r",
|
111
|
1371 (unsigned long) data_map_size, pz_curr_file);
|
0
|
1372
|
|
1373 # ifndef SEPARATE_FIX_PROC
|
|
1374 process_chain_head = NOPROCESS;
|
|
1375
|
|
1376 /* For every fix in our fix list, ... */
|
|
1377 for (; todo_ct > 0; p_fixd++, todo_ct--)
|
|
1378 {
|
|
1379 if (! fix_applies (p_fixd))
|
|
1380 continue;
|
|
1381
|
|
1382 if (VLEVEL( VERB_APPLIES ))
|
|
1383 fprintf (stderr, "Applying %-24s to %s\n",
|
|
1384 p_fixd->fix_name, pz_curr_file);
|
|
1385
|
|
1386 if (p_fixd->fd_flags & FD_REPLACEMENT)
|
|
1387 {
|
|
1388 write_replacement (p_fixd);
|
|
1389 UNLOAD_DATA();
|
|
1390 return;
|
|
1391 }
|
|
1392
|
|
1393 /* IF we do not have a read pointer,
|
|
1394 THEN this is the first fix for the current file.
|
|
1395 Open the source file. That will be used as stdin for
|
|
1396 the first fix. Any subsequent fixes will use the
|
|
1397 stdout descriptor of the previous fix for its stdin. */
|
|
1398
|
|
1399 if (read_fd == -1)
|
|
1400 {
|
|
1401 read_fd = open (pz_curr_file, O_RDONLY);
|
|
1402 if (read_fd < 0)
|
|
1403 {
|
|
1404 fprintf (stderr, "Error %d (%s) opening %s\n", errno,
|
|
1405 xstrerror (errno), pz_curr_file);
|
|
1406 exit (EXIT_FAILURE);
|
|
1407 }
|
|
1408
|
|
1409 /* Ensure we do not get duplicate output */
|
|
1410
|
|
1411 fflush (stdout);
|
|
1412 }
|
|
1413
|
|
1414 read_fd = start_fixer (read_fd, p_fixd, pz_curr_file);
|
|
1415 num_children++;
|
|
1416 }
|
|
1417
|
|
1418 /* IF we have a read-back file descriptor,
|
|
1419 THEN check for changes and write output if changed. */
|
|
1420
|
|
1421 if (read_fd >= 0)
|
|
1422 {
|
|
1423 test_for_changes (read_fd);
|
|
1424 #ifdef DO_STATS
|
|
1425 apply_ct += num_children;
|
|
1426 #endif
|
|
1427 /* Wait for child processes created by chain_open()
|
|
1428 to avoid leaving zombies. */
|
|
1429 do {
|
|
1430 wait ((int *) NULL);
|
|
1431 } while (--num_children > 0);
|
|
1432 }
|
|
1433
|
|
1434 # else /* is SEPARATE_FIX_PROC */
|
|
1435
|
|
1436 for (; todo_ct > 0; p_fixd++, todo_ct--)
|
|
1437 {
|
|
1438 if (! fix_applies (p_fixd))
|
|
1439 continue;
|
|
1440
|
|
1441 if (VLEVEL( VERB_APPLIES ))
|
|
1442 fprintf (stderr, "Applying %-24s to %s\n",
|
|
1443 p_fixd->fix_name, pz_curr_file);
|
|
1444
|
|
1445 if (p_fixd->fd_flags & FD_REPLACEMENT)
|
|
1446 {
|
|
1447 write_replacement (p_fixd);
|
|
1448 UNLOAD_DATA();
|
|
1449 return;
|
|
1450 }
|
|
1451 fix_with_system (p_fixd, pz_curr_file, pz_file_source, pz_temp_file);
|
|
1452 pz_file_source = pz_temp_file;
|
|
1453 }
|
|
1454
|
|
1455 read_fd = open (pz_temp_file, O_RDONLY);
|
|
1456 if (read_fd < 0)
|
|
1457 {
|
|
1458 if (errno != ENOENT)
|
|
1459 fprintf (stderr, "error %d (%s) opening output (%s) for read\n",
|
|
1460 errno, xstrerror (errno), pz_temp_file);
|
|
1461 }
|
|
1462 else
|
|
1463 {
|
|
1464 test_for_changes (read_fd);
|
|
1465 /* Unlinking a file while it is still open is a Bad Idea on
|
|
1466 DOS/Windows. */
|
|
1467 close (read_fd);
|
|
1468 unlink (pz_temp_file);
|
|
1469 }
|
|
1470
|
|
1471 # endif
|
|
1472 UNLOAD_DATA();
|
|
1473 }
|