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