]>
Commit | Line | Data |
---|---|---|
0083c904 | 1 | |
1f414ac4 BK |
2 | /* Install modified versions of certain ANSI-incompatible system header |
3 | files which are fixed to work correctly with ANSI C and placed in a | |
4 | directory that GNU C will search. | |
5 | ||
6 | Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc. | |
7 | ||
8 | This file is part of GNU CC. | |
9 | ||
10 | GNU CC 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 2, or (at your option) | |
13 | any later version. | |
14 | ||
15 | GNU CC 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 GNU CC; see the file COPYING. If not, write to | |
22 | the Free Software Foundation, 59 Temple Place - Suite 330, | |
23 | Boston, MA 02111-1307, USA. */ | |
24 | ||
7b33bb99 | 25 | #include "auto-host.h" |
1f414ac4 | 26 | |
2583397b RL |
27 | #include "gansidecl.h" |
28 | #include "system.h" | |
0083c904 | 29 | #include <signal.h> |
0083c904 | 30 | |
f7d920fd | 31 | #include "gnu-regex.h" |
0083c904 BK |
32 | #include "server.h" |
33 | ||
1f414ac4 BK |
34 | static const char program_id[] = "fixincl version 1.0"; |
35 | ||
36 | #define MINIMUM_MAXIMUM_LINES 128 | |
37 | ||
38 | /* If this particular system's header files define the macro `MAXPATHLEN', | |
39 | we happily take advantage of it; otherwise we use a value which ought | |
40 | to be large enough. */ | |
41 | #ifndef MAXPATHLEN | |
42 | # define MAXPATHLEN 4096 | |
43 | #endif | |
44 | #define NAME_TABLE_SIZE (MINIMUM_MAXIMUM_LINES * MAXPATHLEN) | |
45 | ||
48ac9ce2 AO |
46 | #ifndef EXIT_SUCCESS |
47 | # define EXIT_SUCCESS 0 | |
48 | #endif | |
49 | #ifndef EXIT_FAILURE | |
50 | # define EXIT_FAILURE 1 | |
51 | #endif | |
52 | ||
1f414ac4 BK |
53 | char *file_name_buf; |
54 | ||
0083c904 BK |
55 | #define tSCC static const char |
56 | #define tCC const char | |
57 | #define tSC static char | |
58 | ||
1f414ac4 BK |
59 | typedef int t_success; |
60 | ||
61 | #define FAILURE (-1) | |
62 | #define SUCCESS 0 | |
63 | #define PROBLEM 1 | |
0083c904 | 64 | |
1f414ac4 BK |
65 | #define SUCCEEDED(p) ((p) == SUCCESS) |
66 | #define SUCCESSFUL(p) SUCCEEDED (p) | |
67 | #define FAILED(p) ((p) < SUCCESS) | |
68 | #define HADGLITCH(p) ((p) > SUCCESS) | |
0083c904 | 69 | |
1f414ac4 | 70 | #define NUL '\0' |
0083c904 | 71 | |
1f414ac4 | 72 | /* Test Descriptor |
0083c904 | 73 | |
1f414ac4 BK |
74 | Each fix may have associated tests that determine |
75 | whether the fix needs to be applied or not. | |
76 | Each test has a type (from the te_test_type enumeration); | |
77 | associated test text; and, if the test is TT_EGREP or | |
78 | the negated form TT_NEGREP, a pointer to the compiled | |
79 | version of the text string. | |
80 | ||
81 | */ | |
0083c904 | 82 | typedef enum |
1f414ac4 BK |
83 | { |
84 | TT_TEST, TT_EGREP, TT_NEGREP | |
85 | } te_test_type; | |
0083c904 BK |
86 | |
87 | typedef struct test_desc tTestDesc; | |
88 | ||
89 | struct test_desc | |
1f414ac4 BK |
90 | { |
91 | te_test_type type; | |
92 | const char *pz_test_text; | |
93 | regex_t *p_test_regex; | |
94 | }; | |
0083c904 BK |
95 | |
96 | typedef struct patch_desc tPatchDesc; | |
97 | ||
1f414ac4 BK |
98 | /* Fix Descriptor |
99 | ||
100 | Everything you ever wanted to know about how to apply | |
101 | a particular fix (which files, how to qualify them, | |
102 | how to actually make the fix, etc...) | |
103 | ||
104 | */ | |
0083c904 BK |
105 | #define FD_MACH_ONLY 0x0000 |
106 | #define FD_MACH_IFNOT 0x0001 | |
bb786201 | 107 | #define FD_SHELL_SCRIPT 0x0002 |
0083c904 BK |
108 | #define FD_SKIP_TEST 0x8000 |
109 | ||
110 | typedef struct fix_desc tFixDesc; | |
111 | struct fix_desc | |
1f414ac4 BK |
112 | { |
113 | const char* fix_name; /* Name of the fix */ | |
114 | const char* file_list; /* List of files it applies to */ | |
115 | const char** papz_machs; /* List of machine/os-es it applies to */ | |
116 | regex_t* unused; | |
117 | int test_ct; | |
118 | int fd_flags; | |
119 | tTestDesc* p_test_desc; | |
120 | const char** patch_args; | |
121 | }; | |
122 | ||
123 | /* Working environment strings. Essentially, invocation 'options'. */ | |
124 | char *pz_dest_dir = NULL; | |
125 | char *pz_src_dir = NULL; | |
126 | char *pz_machine = NULL; | |
7d9ccd90 BK |
127 | char *pz_find_base = NULL; |
128 | int find_base_len = 0; | |
1f414ac4 BK |
129 | |
130 | pid_t process_chain_head = (pid_t) -1; | |
131 | ||
132 | const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]"; | |
133 | regex_t incl_quote_re; | |
134 | ||
94db2f71 BK |
135 | char *load_file _P_((const char *)); |
136 | void process _P_((char *, const char *)); | |
d1c6a037 | 137 | void run_compiles (); |
94db2f71 | 138 | void wait_for_pid _P_(( pid_t )); |
d1c6a037 | 139 | void initialize (); |
0083c904 BK |
140 | |
141 | #include "fixincl.x" | |
142 | ||
1f414ac4 BK |
143 | /* * * * * * * * * * * * * * * * * * * |
144 | * | |
145 | * MAIN ROUTINE | |
146 | */ | |
0083c904 BK |
147 | int |
148 | main (argc, argv) | |
149 | int argc; | |
150 | char **argv; | |
151 | { | |
1f414ac4 BK |
152 | static const char gnu_lib_mark[] = |
153 | "This file is part of the GNU C Library"; | |
0083c904 BK |
154 | |
155 | #ifndef NO_BOGOSITY_LIMITS | |
1f414ac4 BK |
156 | # define BOGUS_LIMIT MINIMUM_MAXIMUM_LINES |
157 | size_t loop_ct; | |
0083c904 BK |
158 | #endif |
159 | ||
1f414ac4 BK |
160 | char *apz_names[BOGUS_LIMIT]; |
161 | size_t file_name_ct; | |
0083c904 | 162 | |
1f414ac4 BK |
163 | /* Before anything else, ensure we can allocate our file name buffer. */ |
164 | file_name_buf = (char *) malloc (NAME_TABLE_SIZE); | |
165 | if (file_name_buf == (char *) NULL) | |
0083c904 | 166 | { |
1f414ac4 BK |
167 | fprintf (stderr, "fixincl cannot allocate 0x%08X bytes\n", |
168 | NAME_TABLE_SIZE); | |
169 | exit (EXIT_FAILURE); | |
170 | } | |
0083c904 | 171 | |
1f414ac4 BK |
172 | switch (argc) |
173 | { | |
174 | case 1: | |
175 | break; | |
176 | ||
177 | case 2: | |
0083c904 BK |
178 | if (strcmp (argv[1], "-v") == 0) |
179 | { | |
1f414ac4 BK |
180 | static const char zFmt[] = "echo '%s'"; |
181 | ||
182 | /* The 'version' option is really used to test that: | |
183 | 1. The program loads correctly (no missing libraries) | |
184 | 2. we can correctly run our server shell process | |
185 | 3. that we can compile all the regular expressions. | |
186 | */ | |
187 | run_compiles (); | |
188 | sprintf (file_name_buf, zFmt, program_id); | |
189 | fputs (file_name_buf + 5, stdout); | |
190 | exit (strcmp (run_shell (file_name_buf), program_id)); | |
0083c904 | 191 | } |
0083c904 | 192 | freopen (argv[1], "r", stdin); |
1f414ac4 BK |
193 | break; |
194 | ||
195 | default: | |
196 | fputs ("fixincl ERROR: too many command line arguments\n", stderr); | |
197 | exit (EXIT_FAILURE); | |
0083c904 BK |
198 | } |
199 | ||
d1c6a037 BK |
200 | initialize (); |
201 | ||
202 | #ifndef NO_BOGOSITY_LIMITS | |
203 | /* Some systems only allow so many calls to fork(2). | |
204 | This is inadequate for this program. Consequently, | |
205 | we must let a grandfather process spawn children | |
206 | that then spawn all the processes that do the real work. | |
207 | */ | |
208 | for (;;) | |
209 | { | |
210 | file_name_ct = 0; | |
211 | ||
212 | { | |
213 | char *pz_buf = file_name_buf; | |
214 | ||
215 | /* Only the parent process can read from stdin without confusing | |
216 | the world. (How does the child tell the parent to skip | |
217 | forward? Pipes and files behave differently.) */ | |
218 | ||
219 | while ( (file_name_ct < BOGUS_LIMIT) | |
220 | && (pz_buf < (file_name_buf + NAME_TABLE_SIZE - MAXPATHLEN))) | |
221 | { | |
222 | if (fgets (pz_buf, MAXPATHLEN, stdin) == (char *) NULL) | |
223 | break; | |
92a438d1 | 224 | while (ISSPACE (*pz_buf)) |
d1c6a037 BK |
225 | pz_buf++; |
226 | if ((*pz_buf == '\0') || (*pz_buf == '#')) | |
227 | continue; | |
228 | apz_names[file_name_ct++] = pz_buf; | |
229 | pz_buf += strlen (pz_buf); | |
92a438d1 | 230 | while (ISSPACE (pz_buf[-1])) |
d1c6a037 BK |
231 | pz_buf--; |
232 | *pz_buf++ = '\0'; | |
233 | } | |
234 | } | |
235 | ||
236 | /* IF we did not get any files this time thru | |
237 | THEN we must be done. */ | |
238 | if (file_name_ct == 0) | |
239 | return EXIT_SUCCESS; | |
240 | ||
94db2f71 BK |
241 | fflush (stdout); |
242 | fflush (stderr); | |
243 | ||
d1c6a037 BK |
244 | { |
245 | pid_t child = fork (); | |
246 | if (child == NULLPROCESS) | |
247 | break; | |
248 | ||
249 | if (child == NOPROCESS) | |
250 | { | |
251 | fprintf (stderr, "Error %d (%s) forking in main\n", | |
252 | errno, strerror (errno)); | |
253 | exit (EXIT_FAILURE); | |
254 | } | |
255 | ||
94db2f71 BK |
256 | #ifdef DEBUG |
257 | fprintf (stderr, "Waiting for %d to complete %d files\n", | |
258 | child, file_name_ct); | |
259 | #endif | |
260 | ||
d1c6a037 BK |
261 | wait_for_pid( child, file_name_ct ); |
262 | } | |
263 | } | |
264 | #else | |
18001328 | 265 | /*#*/ error "NON-BOGUS LIMITS NOT SUPPORTED?!?!" |
d1c6a037 BK |
266 | #endif |
267 | ||
d1c6a037 BK |
268 | /* For every file specified in stdandard in |
269 | (except as throttled for bogus reasons)... | |
270 | */ | |
271 | for (loop_ct = 0; loop_ct < file_name_ct; loop_ct++) | |
272 | { | |
273 | char *pz_data; | |
274 | char *pz_file_name = apz_names[loop_ct]; | |
275 | ||
276 | if (access (pz_file_name, R_OK) != 0) | |
277 | { | |
278 | int erno = errno; | |
279 | fprintf (stderr, "Cannot access %s from %s\n\terror %d (%s)\n", | |
280 | pz_file_name, getcwd ((char *) NULL, MAXPATHLEN), | |
281 | erno, strerror (erno)); | |
282 | } | |
283 | else if (pz_data = load_file (pz_file_name), (pz_data != (char *) NULL)) | |
284 | { | |
285 | if (strstr (pz_data, gnu_lib_mark) == (char *) NULL) | |
286 | process (pz_data, pz_file_name); | |
287 | free ((void *) pz_data); | |
288 | } | |
289 | } | |
290 | ||
291 | return EXIT_SUCCESS; | |
292 | } | |
293 | ||
294 | ||
295 | /* * * * * * * * * * * * */ | |
296 | ||
297 | void | |
298 | initialize() | |
299 | { | |
300 | static const char var_not_found[] = | |
301 | "fixincl ERROR: %s environment variable not defined\n"; | |
302 | ||
0083c904 | 303 | { |
1f414ac4 BK |
304 | static const char var[] = "TARGET_MACHINE"; |
305 | pz_machine = getenv (var); | |
306 | if (pz_machine == (char *) NULL) | |
0083c904 | 307 | { |
1f414ac4 | 308 | fprintf (stderr, var_not_found, var); |
0083c904 BK |
309 | exit (EXIT_FAILURE); |
310 | } | |
311 | } | |
312 | ||
313 | { | |
1f414ac4 BK |
314 | static const char var[] = "DESTDIR"; |
315 | pz_dest_dir = getenv (var); | |
316 | if (pz_dest_dir == (char *) NULL) | |
0083c904 | 317 | { |
1f414ac4 | 318 | fprintf (stderr, var_not_found, var); |
0083c904 BK |
319 | exit (EXIT_FAILURE); |
320 | } | |
321 | } | |
322 | ||
323 | { | |
1f414ac4 BK |
324 | static const char var[] = "SRCDIR"; |
325 | pz_src_dir = getenv (var); | |
326 | if (pz_src_dir == (char *) NULL) | |
0083c904 | 327 | { |
1f414ac4 | 328 | fprintf (stderr, var_not_found, var); |
0083c904 BK |
329 | exit (EXIT_FAILURE); |
330 | } | |
331 | } | |
332 | ||
7d9ccd90 BK |
333 | { |
334 | static const char var[] = "FIND_BASE"; | |
335 | pz_find_base = getenv (var); | |
336 | if (pz_find_base == (char *) NULL) | |
337 | { | |
338 | fprintf (stderr, var_not_found, var); | |
339 | exit (EXIT_FAILURE); | |
340 | } | |
341 | find_base_len = strlen( pz_find_base ); | |
342 | } | |
343 | ||
1f414ac4 BK |
344 | /* Compile all the regular expressions now. |
345 | That way, it is done only once for the whole run. | |
346 | */ | |
347 | run_compiles (); | |
0083c904 | 348 | |
1f414ac4 BK |
349 | signal (SIGQUIT, SIG_IGN); |
350 | signal (SIGIOT, SIG_IGN); | |
351 | signal (SIGPIPE, SIG_IGN); | |
352 | signal (SIGALRM, SIG_IGN); | |
353 | signal (SIGTERM, SIG_IGN); | |
0083c904 | 354 | |
d1c6a037 BK |
355 | /* |
356 | Make sure that if we opened a server process, we close it now. | |
357 | This is the grandparent process. We don't need the server anymore | |
358 | and our children should make their own. */ | |
0083c904 | 359 | |
d1c6a037 BK |
360 | close_server (); |
361 | (void)wait ( (int*)NULL ); | |
362 | } | |
0083c904 | 363 | |
d1c6a037 BK |
364 | /* * * * * * * * * * * * * |
365 | ||
366 | wait_for_pid - Keep calling `wait(2)' until it returns | |
367 | the process id we are looking for. Not every system has | |
368 | `waitpid(2)'. We also ensure that the children exit with success. */ | |
7d9ccd90 | 369 | |
d1c6a037 | 370 | void |
48ac9ce2 AO |
371 | wait_for_pid(child, file_name_ct) |
372 | pid_t child; | |
373 | int file_name_ct; | |
d1c6a037 | 374 | { |
d1c6a037 BK |
375 | for (;;) { |
376 | int status; | |
377 | pid_t dead_kid = wait (&status); | |
7d9ccd90 | 378 | |
d1c6a037 BK |
379 | if (dead_kid == child) |
380 | { | |
381 | if (! WIFEXITED( status )) | |
382 | { | |
383 | fprintf (stderr, "child process %d is hung on signal %d\n", | |
384 | child, WSTOPSIG( status )); | |
385 | exit (EXIT_FAILURE); | |
386 | } | |
387 | if (WEXITSTATUS( status ) != 0) | |
388 | { | |
389 | fprintf (stderr, "child process %d exited with status %d\n", | |
390 | child, WEXITSTATUS( status )); | |
391 | exit (EXIT_FAILURE); | |
392 | } | |
d1c6a037 BK |
393 | break; /* normal child completion */ |
394 | } | |
1f414ac4 | 395 | |
d1c6a037 BK |
396 | /* |
397 | IF there is an error, THEN see if it is retryable. | |
398 | If it is not retryable, then break out of this loop. */ | |
399 | if (dead_kid == NOPROCESS) | |
400 | { | |
401 | switch (errno) { | |
402 | case EINTR: | |
403 | case EAGAIN: | |
404 | break; | |
405 | ||
406 | default: | |
407 | fprintf (stderr, "Error %d (%s) waiting for %d to finish\n", | |
408 | errno, strerror( errno ), child ); | |
409 | /* FALLTHROUGH */ | |
410 | ||
411 | case ECHILD: /* no children to wait for?? */ | |
412 | return; | |
0083c904 | 413 | } |
d1c6a037 BK |
414 | } |
415 | } done_waiting:; | |
0083c904 BK |
416 | } |
417 | ||
418 | ||
1f414ac4 BK |
419 | /* * * * * * * * * * * * * |
420 | ||
421 | load_file loads all the contents of a file into malloc-ed memory. | |
422 | Its argument is the name of the file to read in; the returned | |
423 | result is the NUL terminated contents of the file. The file | |
424 | is presumed to be an ASCII text file containing no NULs. */ | |
0083c904 | 425 | char * |
1f414ac4 BK |
426 | load_file (pz_file_name) |
427 | const char *pz_file_name; | |
0083c904 | 428 | { |
1f414ac4 BK |
429 | char *pz_data; |
430 | size_t file_size; | |
0083c904 BK |
431 | |
432 | { | |
433 | struct stat stbf; | |
1f414ac4 BK |
434 | |
435 | if (stat (pz_file_name, &stbf) != 0) | |
0083c904 BK |
436 | { |
437 | fprintf (stderr, "error %d (%s) stat-ing %s\n", | |
1f414ac4 | 438 | errno, strerror (errno), pz_file_name); |
0083c904 BK |
439 | return (char *) NULL; |
440 | } | |
1f414ac4 | 441 | file_size = stbf.st_size; |
0083c904 | 442 | } |
1f414ac4 | 443 | if (file_size == 0) |
0083c904 BK |
444 | return (char *) NULL; |
445 | ||
1f414ac4 BK |
446 | pz_data = (char *) malloc ((file_size + 16) & ~0x00F); |
447 | if (pz_data == (char *) NULL) | |
0083c904 BK |
448 | { |
449 | fprintf (stderr, "error: could not malloc %d bytes\n", | |
1f414ac4 | 450 | file_size); |
0083c904 BK |
451 | exit (EXIT_FAILURE); |
452 | } | |
453 | ||
454 | { | |
1f414ac4 BK |
455 | FILE *fp = fopen (pz_file_name, "r"); |
456 | size_t size_left = file_size; | |
457 | char *read_ptr = pz_data; | |
0083c904 BK |
458 | |
459 | if (fp == (FILE *) NULL) | |
460 | { | |
461 | fprintf (stderr, "error %d (%s) opening %s\n", errno, | |
1f414ac4 BK |
462 | strerror (errno), pz_file_name); |
463 | free ((void *) pz_data); | |
0083c904 BK |
464 | return (char *) NULL; |
465 | } | |
466 | ||
467 | do | |
468 | { | |
1f414ac4 | 469 | size_t sizeRead = fread ((void *) read_ptr, 1, size_left, fp); |
0083c904 BK |
470 | |
471 | if (sizeRead == 0) | |
472 | { | |
473 | if (feof (fp)) | |
474 | break; | |
475 | ||
476 | if (ferror (fp)) | |
477 | { | |
7d9ccd90 BK |
478 | int err = errno; |
479 | if (err != EISDIR) | |
480 | fprintf (stderr, "error %d (%s) reading %s\n", err, | |
481 | strerror (err), pz_file_name); | |
1f414ac4 | 482 | free ((void *) pz_data); |
0083c904 BK |
483 | fclose (fp); |
484 | return (char *) NULL; | |
485 | } | |
486 | } | |
487 | ||
1f414ac4 BK |
488 | read_ptr += sizeRead; |
489 | size_left -= sizeRead; | |
0083c904 | 490 | } |
1f414ac4 | 491 | while (size_left != 0); |
0083c904 | 492 | |
1f414ac4 | 493 | *read_ptr = '\0'; |
0083c904 | 494 | fclose (fp); |
0083c904 | 495 | } |
1f414ac4 | 496 | return pz_data; |
0083c904 BK |
497 | } |
498 | ||
499 | ||
1f414ac4 BK |
500 | /* * * * * * * * * * * * * |
501 | ||
502 | run_compiles run all the regexp compiles for all the fixes once. | |
503 | */ | |
0083c904 | 504 | void |
1f414ac4 | 505 | run_compiles () |
0083c904 | 506 | { |
94db2f71 BK |
507 | tSCC z_bad_comp[] = "fixincl ERROR: cannot compile %s regex for %s\n\ |
508 | \texpr = `%s'\n\terror %s\n"; | |
1f414ac4 BK |
509 | tFixDesc *p_fixd = fixDescList; |
510 | int fix_ct = FIX_COUNT; | |
511 | tTestDesc *p_test; | |
512 | int test_ct; | |
513 | int re_ct = REGEX_COUNT; | |
514 | const char *pz_err; | |
515 | regex_t *p_re = (regex_t *) malloc (REGEX_COUNT * sizeof (regex_t)); | |
516 | ||
517 | if (p_re == (regex_t *) NULL) | |
0083c904 BK |
518 | { |
519 | fprintf (stderr, "fixincl ERROR: cannot allocate %d bytes for regex\n", | |
520 | REGEX_COUNT * sizeof (regex_t)); | |
521 | exit (EXIT_FAILURE); | |
522 | } | |
523 | ||
d1c6a037 BK |
524 | /* Make sure re_compile_pattern does not stumble across invalid |
525 | data */ | |
526 | ||
527 | memset ( (void*)p_re, '\0', REGEX_COUNT * sizeof (regex_t) ); | |
528 | memset ( (void*)&incl_quote_re, '\0', sizeof (regex_t) ); | |
529 | ||
1f414ac4 BK |
530 | /* The patterns we search for are all egrep patterns. |
531 | In the shell version of this program, we invoke egrep | |
532 | with the supplied pattern. Here, we will run | |
533 | re_compile_pattern, but it must be using the same rules. */ | |
534 | ||
0083c904 | 535 | re_set_syntax (RE_SYNTAX_EGREP); |
1f414ac4 BK |
536 | pz_err = re_compile_pattern (incl_quote_pat, sizeof (incl_quote_pat)-1, |
537 | &incl_quote_re); | |
538 | if (pz_err != (char *) NULL) | |
0083c904 | 539 | { |
1f414ac4 BK |
540 | fprintf (stderr, z_bad_comp, "quoted include", "run_compiles", |
541 | incl_quote_pat, pz_err); | |
0083c904 BK |
542 | exit (EXIT_FAILURE); |
543 | } | |
544 | ||
1f414ac4 | 545 | /* FOR every fixup, ... */ |
0083c904 BK |
546 | do |
547 | { | |
1f414ac4 BK |
548 | p_test = p_fixd->p_test_desc; |
549 | test_ct = p_fixd->test_ct; | |
0083c904 | 550 | |
1f414ac4 BK |
551 | /* IF the machine type pointer is not NULL (we are not in test mode) |
552 | AND this test is for or not done on particular machines | |
553 | THEN ... */ | |
554 | ||
555 | if ( (pz_machine != NULL) | |
556 | && (p_fixd->papz_machs != (const char**) NULL) ) | |
557 | { | |
558 | const char **papz_machs = p_fixd->papz_machs; | |
559 | char *pz = file_name_buf; | |
560 | char *pz_sep = ""; | |
561 | tCC *pz_if_true; | |
562 | tCC *pz_if_false; | |
563 | tSCC skip[] = "skip"; | |
564 | tSCC run[] = "run"; | |
565 | ||
566 | /* Construct a shell script that looks like this: | |
567 | ||
568 | case our-cpu-platform-os in | |
569 | tests-cpu-platform-os-pattern ) | |
570 | echo run ;; | |
571 | * ) | |
572 | echo skip ;; | |
573 | esac | |
574 | ||
575 | where 'run' and 'skip' may be reversed, depending on | |
576 | the sense of the test. */ | |
577 | ||
578 | sprintf (pz, "case %s in\n", pz_machine); | |
579 | pz += strlen (pz); | |
580 | ||
581 | if (p_fixd->fd_flags & FD_MACH_IFNOT) | |
582 | { | |
583 | pz_if_true = skip; | |
584 | pz_if_false = run; | |
585 | } | |
586 | else | |
587 | { | |
588 | pz_if_true = run; | |
589 | pz_if_false = skip; | |
590 | } | |
591 | ||
592 | /* FOR any additional machine names to test for, | |
593 | insert the " | \\\n" glue and the machine pattern. */ | |
594 | ||
595 | for (;;) | |
596 | { | |
597 | const char* pz_mach = *(papz_machs++); | |
598 | ||
599 | if (pz_mach == (const char*) NULL) | |
600 | break; | |
601 | sprintf (pz, "%s %s", pz_sep, pz_mach); | |
602 | pz += strlen (pz); | |
603 | pz_sep = " | \\\n"; | |
604 | } | |
605 | sprintf (pz, " )\n echo %s ;;\n * )\n echo %s ;;\nesac", | |
606 | pz_if_true, pz_if_false); | |
607 | ||
608 | /* Run the script. | |
609 | The result will start either with 's' or 'r'. */ | |
610 | ||
7d032a4f RO |
611 | { |
612 | int skip; | |
613 | pz = run_shell (file_name_buf); | |
614 | skip = (*pz == 's'); | |
615 | free ( (void*)pz ); | |
616 | if (skip) | |
617 | { | |
618 | p_fixd->fd_flags |= FD_SKIP_TEST; | |
619 | continue; | |
620 | } | |
621 | } | |
0083c904 | 622 | } |
0083c904 | 623 | |
1f414ac4 BK |
624 | /* FOR every test for the fixup, ... */ |
625 | ||
626 | while (--test_ct >= 0) | |
0083c904 | 627 | { |
1f414ac4 | 628 | switch (p_test->type) |
0083c904 BK |
629 | { |
630 | case TT_EGREP: | |
631 | case TT_NEGREP: | |
1f414ac4 BK |
632 | /* You might consider putting the following under #ifdef. |
633 | The number of re's used is computed by autogen. | |
634 | So, it is static and known at compile time. */ | |
635 | ||
636 | if (--re_ct < 0) | |
0083c904 BK |
637 | { |
638 | fputs ("out of RE's\n", stderr); | |
639 | exit (EXIT_FAILURE); | |
640 | } | |
641 | ||
1f414ac4 BK |
642 | p_test->p_test_regex = p_re++; |
643 | pz_err = re_compile_pattern (p_test->pz_test_text, | |
644 | strlen (p_test->pz_test_text), | |
645 | p_test->p_test_regex); | |
646 | if (pz_err != (char *) NULL) | |
0083c904 | 647 | { |
1f414ac4 BK |
648 | fprintf (stderr, z_bad_comp, "select test", p_fixd->fix_name, |
649 | p_test->pz_test_text, pz_err); | |
0083c904 BK |
650 | exit (EXIT_FAILURE); |
651 | } | |
652 | } | |
1f414ac4 | 653 | p_test++; |
0083c904 BK |
654 | } |
655 | } | |
1f414ac4 | 656 | while (p_fixd++, --fix_ct > 0); |
0083c904 BK |
657 | } |
658 | ||
659 | ||
1f414ac4 BK |
660 | /* * * * * * * * * * * * * |
661 | ||
662 | create_file Create the output modified file. | |
663 | Input: the name of the file to create | |
664 | Returns: a file pointer to the new, open file */ | |
665 | ||
666 | #define S_IRALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) | |
667 | ||
0083c904 | 668 | FILE * |
1f414ac4 BK |
669 | create_file (pz_file_name) |
670 | const char *pz_file_name; | |
0083c904 BK |
671 | { |
672 | int fd; | |
673 | FILE *pf; | |
674 | char fname[MAXPATHLEN]; | |
675 | ||
7d9ccd90 BK |
676 | #ifdef DEBUG |
677 | if (strncmp( pz_file_name, pz_find_base, find_base_len ) != 0) | |
678 | { | |
679 | fprintf (stderr, "Error: input file %s does not match %s/*\n", | |
680 | pz_file_name, pz_find_base ); | |
681 | exit (1); | |
682 | } | |
683 | #endif | |
684 | ||
685 | sprintf (fname, "%s/%s", pz_dest_dir, pz_file_name + find_base_len); | |
0083c904 | 686 | |
1f414ac4 | 687 | fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL); |
0083c904 | 688 | |
1f414ac4 | 689 | /* We may need to create the directories needed... */ |
0083c904 BK |
690 | if ((fd < 0) && (errno == ENOENT)) |
691 | { | |
1f414ac4 | 692 | char *pz_dir = strchr (fname + 1, '/'); |
0083c904 BK |
693 | struct stat stbf; |
694 | ||
1f414ac4 | 695 | while (pz_dir != (char *) NULL) |
0083c904 | 696 | { |
1f414ac4 | 697 | *pz_dir = NUL; |
0083c904 BK |
698 | if (stat (fname, &stbf) < 0) |
699 | { | |
700 | mkdir (fname, S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | |
701 | | S_IROTH | S_IXOTH); | |
702 | } | |
703 | ||
1f414ac4 BK |
704 | *pz_dir = '/'; |
705 | pz_dir = strchr (pz_dir + 1, '/'); | |
0083c904 | 706 | } |
1f414ac4 BK |
707 | |
708 | /* Now, lets try the open again... */ | |
709 | fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRALL); | |
0083c904 BK |
710 | } |
711 | if (fd < 0) | |
712 | { | |
713 | fprintf (stderr, "Error %d (%s) creating %s\n", | |
714 | errno, strerror (errno), fname); | |
715 | exit (EXIT_FAILURE); | |
716 | } | |
1f414ac4 | 717 | fprintf (stderr, "Fixed: %s\n", pz_file_name); |
0083c904 BK |
718 | pf = fdopen (fd, "w"); |
719 | ||
720 | #ifdef LATER | |
721 | { | |
1f414ac4 BK |
722 | static const char hdr[] = |
723 | "/* DO NOT EDIT THIS FILE.\n\n" | |
724 | " It has been auto-edited by fixincludes from /usr/include/%s\n" | |
725 | " This had to be done to correct non-standard usages in the\n" | |
726 | " original, manufacturer supplied header file. */\n\n"; | |
727 | ||
728 | fprintf (pf, hdr, pz_file_name); | |
0083c904 BK |
729 | } |
730 | #endif | |
731 | return pf; | |
732 | } | |
733 | ||
0083c904 | 734 | |
1f414ac4 | 735 | /* * * * * * * * * * * * * |
0083c904 | 736 | |
1f414ac4 BK |
737 | test_test make sure a shell-style test expression passes. |
738 | Input: a pointer to the descriptor of the test to run and | |
739 | the name of the file that we might want to fix | |
740 | Result: SUCCESS or FAILURE, depending on the result of the | |
741 | shell script we run. */ | |
742 | ||
743 | t_success | |
744 | test_test (p_test, pz_file_name) | |
745 | tTestDesc *p_test; | |
746 | char* pz_file_name; | |
747 | { | |
94db2f71 BK |
748 | tSCC cmd_fmt[] = |
749 | "file=%s\n\ | |
750 | if ( test %s ) > /dev/null 2>&1\n\ | |
751 | then echo TRUE\n\ | |
752 | else echo FALSE\n\ | |
753 | fi"; | |
754 | ||
1f414ac4 BK |
755 | char *pz_res; |
756 | t_success res = FAILURE; | |
757 | ||
758 | static char cmd_buf[4096]; | |
1f414ac4 BK |
759 | |
760 | sprintf (cmd_buf, cmd_fmt, pz_file_name, p_test->pz_test_text); | |
761 | pz_res = run_shell (cmd_buf); | |
762 | if (*pz_res == 'T') | |
0083c904 | 763 | res = SUCCESS; |
1f414ac4 | 764 | free ((void *) pz_res); |
0083c904 BK |
765 | return res; |
766 | } | |
767 | ||
768 | ||
1f414ac4 BK |
769 | /* * * * * * * * * * * * * |
770 | ||
771 | egrep_test make sure an egrep expression is found in the file text. | |
772 | Input: a pointer to the descriptor of the test to run and | |
773 | the pointer to the contents of the file under suspicion | |
774 | Result: SUCCESS if the pattern is found, FAILURE otherwise | |
775 | ||
776 | The caller may choose 'FAILURE' as 'SUCCESS' if the sense of the test | |
777 | is inverted. */ | |
778 | ||
779 | t_success | |
780 | egrep_test (pz_data, p_test) | |
781 | char *pz_data; | |
782 | tTestDesc *p_test; | |
0083c904 BK |
783 | { |
784 | regmatch_t match; | |
1f414ac4 | 785 | |
0083c904 | 786 | #ifndef NO_BOGOSITY |
1f414ac4 BK |
787 | if (p_test->p_test_regex == 0) |
788 | fprintf (stderr, "fixincl ERROR RE not compiled: `%s'\n", | |
789 | p_test->pz_test_text); | |
0083c904 | 790 | #endif |
1f414ac4 | 791 | if (regexec (p_test->p_test_regex, pz_data, 1, &match, 0) == 0) |
0083c904 BK |
792 | return SUCCESS; |
793 | return FAILURE; | |
794 | } | |
795 | ||
796 | ||
94db2f71 BK |
797 | /* * * * * * * * * * * * * |
798 | ||
799 | quoted_file_exists Make sure that a file exists before we emit | |
800 | the file name. If we emit the name, our invoking shell will try | |
801 | to copy a non-existing file into the destination directory. */ | |
802 | ||
803 | int | |
804 | quoted_file_exists (pz_src_path, pz_file_path, pz_file) | |
805 | char* pz_src_path; | |
806 | char* pz_file_path; | |
807 | char* pz_file; | |
808 | { | |
809 | char z[ MAXPATHLEN ]; | |
810 | char* pz; | |
811 | sprintf (z, "%s/%s/", pz_src_path, pz_file_path); | |
812 | pz = z + strlen ( z ); | |
813 | ||
814 | for (;;) { | |
815 | char ch = *pz_file++; | |
92a438d1 | 816 | if (! ISGRAPH(ch)) |
94db2f71 BK |
817 | return 0; |
818 | if (ch == '"') | |
819 | break; | |
820 | *pz++ = ch; | |
821 | } | |
822 | *pz = '\0'; | |
823 | { | |
824 | struct stat s; | |
825 | if (stat (z, &s) != 0) | |
826 | return 0; | |
827 | return S_ISREG( s.st_mode ); | |
828 | } | |
829 | } | |
830 | ||
831 | ||
1f414ac4 BK |
832 | /* * * * * * * * * * * * * |
833 | * | |
834 | extract_quoted_files | |
835 | ||
836 | The syntax, `#include "file.h"' specifies that the compiler is to | |
837 | search the local directory of the current file before the include | |
838 | list. Consequently, if we have modified a header and stored it in | |
839 | another directory, any files that are included by that modified | |
840 | file in that fashion must also be copied into this new directory. | |
841 | This routine finds those flavors of #include and for each one found | |
842 | emits a triple of: | |
843 | ||
844 | 1. source directory of the original file | |
845 | 2. the relative path file name of the #includ-ed file | |
846 | 3. the full destination path for this file | |
847 | ||
848 | Input: the text of the file, the file name and a pointer to the | |
849 | match list where the match information was stored. | |
850 | Result: internally nothing. The results are written to stdout | |
851 | for interpretation by the invoking shell */ | |
0083c904 | 852 | |
94db2f71 | 853 | |
0083c904 | 854 | void |
1f414ac4 BK |
855 | extract_quoted_files (pz_data, pz_file_name, p_re_match) |
856 | char *pz_data; | |
857 | const char *pz_file_name; | |
858 | regmatch_t *p_re_match; | |
0083c904 | 859 | { |
1f414ac4 BK |
860 | char *pz_dir_end = strrchr (pz_file_name, '/'); |
861 | char *pz_incl_quot = pz_data; | |
0083c904 | 862 | |
1f414ac4 | 863 | fprintf (stderr, "Quoted includes in %s\n", pz_file_name); |
0083c904 | 864 | |
1f414ac4 | 865 | /* Set "pz_file_name" to point to the containing subdirectory of the source |
94db2f71 | 866 | If there is none, then it is in our current directory, ".". */ |
1f414ac4 BK |
867 | |
868 | if (pz_dir_end == (char *) NULL) | |
869 | pz_file_name = "."; | |
0083c904 | 870 | else |
1f414ac4 | 871 | *pz_dir_end = '\0'; |
0083c904 BK |
872 | |
873 | for (;;) | |
874 | { | |
1f414ac4 BK |
875 | pz_incl_quot += p_re_match->rm_so; |
876 | ||
877 | /* Skip forward to the included file name */ | |
92a438d1 | 878 | while (ISSPACE (*pz_incl_quot)) |
1f414ac4 | 879 | pz_incl_quot++; |
92a438d1 KG |
880 | /* ISSPACE() may evaluate is argument more than once! */ |
881 | while ((++pz_incl_quot, ISSPACE (*pz_incl_quot))) | |
1f414ac4 BK |
882 | ; |
883 | pz_incl_quot += sizeof ("include") - 1; | |
884 | while (*pz_incl_quot++ != '"') | |
885 | ; | |
886 | ||
94db2f71 BK |
887 | if (quoted_file_exists (pz_src_dir, pz_file_name, pz_incl_quot)) |
888 | { | |
889 | /* Print the source directory and the subdirectory | |
890 | of the file in question. */ | |
891 | printf ("%s %s/", pz_src_dir, pz_file_name); | |
892 | pz_dir_end = pz_incl_quot; | |
893 | ||
894 | /* Append to the directory the relative path of the desired file */ | |
895 | while (*pz_incl_quot != '"') | |
896 | putc (*pz_incl_quot++, stdout); | |
897 | ||
898 | /* Now print the destination directory appended with the | |
899 | relative path of the desired file */ | |
900 | printf (" %s/%s/", pz_dest_dir, pz_file_name); | |
901 | while (*pz_dir_end != '"') | |
902 | putc (*pz_dir_end++, stdout); | |
903 | ||
904 | /* End of entry */ | |
905 | putc ('\n', stdout); | |
906 | } | |
0083c904 | 907 | |
1f414ac4 BK |
908 | /* Find the next entry */ |
909 | if (regexec (&incl_quote_re, pz_incl_quot, 1, p_re_match, 0) != 0) | |
0083c904 BK |
910 | break; |
911 | } | |
912 | } | |
913 | ||
914 | ||
bb786201 BK |
915 | |
916 | /* * * * * * * * * * * * * | |
917 | ||
918 | This loop should only cycle for 1/2 of one loop. | |
919 | "chain_open" starts a process that uses "read_fd" as | |
920 | its stdin and returns the new fd this process will use | |
921 | for stdout. */ | |
922 | ||
923 | int | |
924 | start_fixer (read_fd, p_fixd, pz_file_name) | |
925 | int read_fd; | |
926 | tFixDesc* p_fixd; | |
927 | char* pz_file_name; | |
928 | { | |
929 | tSCC z_err[] = "Error %d (%s) starting filter process for %s\n"; | |
930 | tCC* pz_cmd_save; | |
931 | char* pz_cmd; | |
932 | ||
933 | if ((p_fixd->fd_flags & FD_SHELL_SCRIPT) == 0) | |
934 | pz_cmd = (char*)NULL; | |
935 | else | |
936 | { | |
937 | tSCC z_cmd_fmt[] = "file='%s'\n%s"; | |
938 | pz_cmd = (char*)xmalloc (strlen (p_fixd->patch_args[2]) | |
939 | + sizeof( z_cmd_fmt ) | |
940 | + strlen( pz_file_name )); | |
941 | sprintf (pz_cmd, z_cmd_fmt, pz_file_name, p_fixd->patch_args[2]); | |
942 | pz_cmd_save = p_fixd->patch_args[2]; | |
943 | p_fixd->patch_args[2] = pz_cmd; | |
944 | } | |
945 | ||
946 | for (;;) | |
947 | { | |
948 | static int failCt = 0; | |
949 | int fd; | |
950 | ||
951 | fd = chain_open (read_fd, | |
952 | (t_pchar *) p_fixd->patch_args, | |
953 | (process_chain_head == -1) | |
954 | ? &process_chain_head : (pid_t *) NULL); | |
955 | ||
956 | if (fd != -1) | |
957 | { | |
958 | read_fd = fd; | |
959 | break; | |
960 | } | |
961 | ||
962 | fprintf (stderr, z_err, errno, strerror (errno), | |
963 | p_fixd->fix_name); | |
964 | ||
965 | if ((errno != EAGAIN) || (++failCt > 10)) | |
966 | exit (EXIT_FAILURE); | |
967 | sleep (1); | |
968 | } | |
969 | ||
970 | if (pz_cmd != (char*)NULL) | |
971 | { | |
972 | free ((void*)pz_cmd); | |
973 | p_fixd->patch_args[2] = pz_cmd_save; | |
974 | } | |
975 | ||
976 | return read_fd; | |
977 | } | |
978 | ||
1f414ac4 BK |
979 | /* * * * * * * * * * * * * |
980 | ||
981 | Process the potential fixes for a particular include file. | |
982 | Input: the original text of the file and the file's name | |
983 | Result: none. A new file may or may not be created. */ | |
984 | ||
0083c904 | 985 | void |
1f414ac4 BK |
986 | process (pz_data, pz_file_name) |
987 | char *pz_data; | |
988 | const char *pz_file_name; | |
0083c904 | 989 | { |
48ac9ce2 | 990 | static char env_current_file[1024]; |
1f414ac4 BK |
991 | tFixDesc *p_fixd = fixDescList; |
992 | int todo_ct = FIX_COUNT; | |
48ac9ce2 | 993 | int read_fd = -1; |
9f8eec39 | 994 | int num_children = 0; |
0083c904 | 995 | |
1f414ac4 | 996 | process_chain_head = NOPROCESS; |
94db2f71 | 997 | fprintf (stderr, "%-50s \r", pz_file_name ); |
1f414ac4 BK |
998 | /* For every fix in our fix list, ... */ |
999 | for (; todo_ct > 0; p_fixd++, todo_ct--) | |
0083c904 | 1000 | { |
1f414ac4 BK |
1001 | tTestDesc *p_test; |
1002 | int test_ct; | |
0083c904 | 1003 | |
1f414ac4 | 1004 | if (p_fixd->fd_flags & FD_SKIP_TEST) |
0083c904 BK |
1005 | continue; |
1006 | ||
1f414ac4 BK |
1007 | /* IF there is a file name restriction, |
1008 | THEN ensure the current file name matches one in the pattern */ | |
1009 | ||
1010 | if (p_fixd->file_list != (char *) NULL) | |
0083c904 | 1011 | { |
1f414ac4 BK |
1012 | const char *pz_fname = pz_file_name; |
1013 | const char *pz_scan = p_fixd->file_list; | |
1014 | size_t name_len; | |
0083c904 | 1015 | |
1f414ac4 BK |
1016 | while ((pz_fname[0] == '.') && (pz_fname[1] == '/')) |
1017 | pz_fname += 2; | |
1018 | name_len = strlen (pz_fname); | |
0083c904 BK |
1019 | |
1020 | for (;;) | |
1021 | { | |
1f414ac4 BK |
1022 | pz_scan = strstr (pz_scan + 1, pz_fname); |
1023 | /* IF we can't match the string at all, | |
1024 | THEN bail */ | |
1025 | if (pz_scan == (char *) NULL) | |
1026 | goto next_fix; | |
0083c904 | 1027 | |
1f414ac4 BK |
1028 | /* IF the match is surrounded by the '|' markers, |
1029 | THEN we found a full match -- time to run the tests */ | |
1030 | ||
1031 | if ((pz_scan[-1] == '|') && (pz_scan[name_len] == '|')) | |
0083c904 BK |
1032 | break; |
1033 | } | |
1034 | } | |
1035 | ||
1f414ac4 BK |
1036 | /* FOR each test, see if it fails. |
1037 | IF it does fail, then we go on to the next test */ | |
0083c904 | 1038 | |
1f414ac4 BK |
1039 | for (p_test = p_fixd->p_test_desc, test_ct = p_fixd->test_ct; |
1040 | test_ct-- > 0; | |
1041 | p_test++) | |
0083c904 | 1042 | { |
94db2f71 | 1043 | #ifdef DEBUG_TEST |
1f414ac4 BK |
1044 | static const char z_test_fail[] = |
1045 | "%16s test %2d failed for %s\n"; | |
1046 | #endif | |
1047 | switch (p_test->type) | |
0083c904 BK |
1048 | { |
1049 | case TT_TEST: | |
1f414ac4 BK |
1050 | if (!SUCCESSFUL (test_test (p_test, pz_file_name))) |
1051 | { | |
94db2f71 | 1052 | #ifdef DEBUG_TEST |
1f414ac4 BK |
1053 | fprintf (stderr, z_test_fail, p_fixd->fix_name, |
1054 | p_fixd->test_ct - test_ct, pz_file_name); | |
1055 | #endif | |
1056 | goto next_fix; | |
1057 | } | |
0083c904 BK |
1058 | break; |
1059 | ||
1060 | case TT_EGREP: | |
1f414ac4 BK |
1061 | if (!SUCCESSFUL (egrep_test (pz_data, p_test))) |
1062 | { | |
94db2f71 | 1063 | #ifdef DEBUG_TEST |
1f414ac4 BK |
1064 | fprintf (stderr, z_test_fail, p_fixd->fix_name, |
1065 | p_fixd->test_ct - test_ct, pz_file_name); | |
1066 | #endif | |
1067 | goto next_fix; | |
1068 | } | |
0083c904 BK |
1069 | break; |
1070 | ||
1071 | case TT_NEGREP: | |
1f414ac4 BK |
1072 | if (SUCCESSFUL (egrep_test (pz_data, p_test))) |
1073 | { | |
94db2f71 | 1074 | #ifdef DEBUG_TEST |
1f414ac4 BK |
1075 | fprintf (stderr, z_test_fail, p_fixd->fix_name, |
1076 | p_fixd->test_ct - test_ct, pz_file_name); | |
1077 | #endif | |
1078 | goto next_fix; | |
1079 | } | |
0083c904 BK |
1080 | break; |
1081 | } | |
1082 | } | |
1083 | ||
7d9ccd90 | 1084 | fprintf (stderr, "Applying %-24s to %s\n", |
1f414ac4 BK |
1085 | p_fixd->fix_name, pz_file_name); |
1086 | ||
1087 | /* IF we do not have a read pointer, | |
1088 | THEN this is the first fix for the current file. | |
1089 | Open the source file. That will be used as stdin for | |
1090 | the first fix. Any subsequent fixes will use the | |
1091 | stdout descriptor of the previous fix as its stdin. */ | |
0083c904 | 1092 | |
48ac9ce2 | 1093 | if (read_fd == -1) |
1f414ac4 | 1094 | { |
48ac9ce2 AO |
1095 | read_fd = open (pz_file_name, O_RDONLY); |
1096 | if (read_fd < 0) | |
0083c904 | 1097 | { |
1f414ac4 BK |
1098 | fprintf (stderr, "Error %d (%s) opening %s\n", errno, |
1099 | strerror (errno), pz_file_name); | |
1100 | exit (EXIT_FAILURE); | |
0083c904 | 1101 | } |
1f414ac4 | 1102 | } |
0083c904 | 1103 | |
bb786201 BK |
1104 | read_fd = start_fixer (read_fd, p_fixd, pz_file_name); |
1105 | num_children++; | |
0083c904 | 1106 | |
1f414ac4 BK |
1107 | next_fix: |
1108 | ; | |
0083c904 BK |
1109 | } |
1110 | ||
1f414ac4 BK |
1111 | /* IF after all the tests we did not start any patch programs, |
1112 | THEN quit now. */ | |
1113 | ||
48ac9ce2 | 1114 | if (read_fd < 0) |
0083c904 BK |
1115 | return; |
1116 | ||
1f414ac4 BK |
1117 | /* OK. We have work to do. Read back in the output |
1118 | of the filtering chain. Compare each byte as we read it with | |
1119 | the contents of the original file. As soon as we find any | |
1120 | difference, we will create the output file, write out all | |
1121 | the matched text and then copy any remaining data from the | |
1122 | output of the filter chain. | |
1123 | */ | |
0083c904 | 1124 | { |
48ac9ce2 | 1125 | FILE *in_fp = fdopen (read_fd, "r"); |
1f414ac4 BK |
1126 | FILE *out_fp = (FILE *) NULL; |
1127 | char *pz_cmp = pz_data; | |
0083c904 BK |
1128 | |
1129 | for (;;) | |
1130 | { | |
1131 | int ch; | |
1132 | ||
1f414ac4 | 1133 | ch = getc (in_fp); |
0083c904 BK |
1134 | if (ch == EOF) |
1135 | break; | |
1136 | ||
1f414ac4 BK |
1137 | /* IF we are emitting the output |
1138 | THEN emit this character, too. | |
1139 | */ | |
1140 | if (out_fp != (FILE *) NULL) | |
1141 | putc (ch, out_fp); | |
0083c904 | 1142 | |
1f414ac4 BK |
1143 | /* ELSE if this character does not match the original, |
1144 | THEN now is the time to start the output. | |
1145 | */ | |
1146 | else if (ch != *pz_cmp) | |
0083c904 | 1147 | { |
1f414ac4 BK |
1148 | out_fp = create_file (pz_file_name); |
1149 | ||
1150 | /* IF there are matched data, write it all now. */ | |
1151 | if (pz_cmp != pz_data) | |
0083c904 | 1152 | { |
1f414ac4 BK |
1153 | char c = *pz_cmp; |
1154 | ||
1155 | *pz_cmp = NUL; | |
1156 | fputs (pz_data, out_fp); | |
1157 | *pz_cmp = c; | |
0083c904 | 1158 | } |
0083c904 | 1159 | |
1f414ac4 BK |
1160 | /* Emit the current unmatching character */ |
1161 | putc (ch, out_fp); | |
0083c904 BK |
1162 | } |
1163 | else | |
1f414ac4 BK |
1164 | /* ELSE the character matches. Advance the compare ptr */ |
1165 | pz_cmp++; | |
0083c904 BK |
1166 | } |
1167 | ||
1f414ac4 BK |
1168 | /* IF we created the output file, ... */ |
1169 | if (out_fp != (FILE *) NULL) | |
0083c904 BK |
1170 | { |
1171 | regmatch_t match; | |
1172 | ||
1f414ac4 BK |
1173 | /* Close the file and see if we have to worry about |
1174 | `#include "file.h"' constructs. */ | |
1175 | fclose (out_fp); | |
1176 | if (regexec (&incl_quote_re, pz_data, 1, &match, 0) == 0) | |
1177 | extract_quoted_files (pz_data, pz_file_name, &match); | |
0083c904 | 1178 | } |
1f414ac4 | 1179 | fclose (in_fp); |
0083c904 | 1180 | } |
48ac9ce2 | 1181 | close (read_fd); /* probably redundant, but I'm paranoid */ |
9f8eec39 RO |
1182 | |
1183 | /* Wait for child processes created by chain_open() | |
1184 | to avoid creating zombies. */ | |
1185 | while (--num_children >= 0) | |
1186 | wait ((int *) NULL); | |
0083c904 | 1187 | } |