| 1 | |
| 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 GCC will search. |
| 5 | |
| 6 | Copyright (C) 1999, 2000, 2001, 2004, 2009 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 | /* * * * * * * * * * * * * |
| 27 | |
| 28 | load_file_data loads all the contents of a file into malloc-ed memory. |
| 29 | Its argument is the file pointer of the file to read in; the returned |
| 30 | result is the NUL terminated contents of the file. The file |
| 31 | is presumed to be an ASCII text file containing no NULs. */ |
| 32 | |
| 33 | char * |
| 34 | load_file_data (FILE* fp) |
| 35 | { |
| 36 | char *pz_data = (char*)NULL; |
| 37 | int space_left = -1; /* allow for terminating NUL */ |
| 38 | size_t space_used = 0; |
| 39 | |
| 40 | if (fp == (FILE*)NULL) |
| 41 | return pz_data; |
| 42 | |
| 43 | do |
| 44 | { |
| 45 | size_t size_read; |
| 46 | |
| 47 | if (space_left < 1024) |
| 48 | { |
| 49 | space_left += 4096; |
| 50 | pz_data = XRESIZEVEC (char, pz_data, space_left + space_used + 1 ); |
| 51 | } |
| 52 | size_read = fread (pz_data + space_used, 1, space_left, fp); |
| 53 | |
| 54 | if (size_read == 0) |
| 55 | { |
| 56 | if (feof (fp)) |
| 57 | break; |
| 58 | |
| 59 | if (ferror (fp)) |
| 60 | { |
| 61 | int err = errno; |
| 62 | if (err != EISDIR) |
| 63 | fprintf (stderr, format: "error %d (%s) reading input\n" , err, |
| 64 | xstrerror (err)); |
| 65 | free (ptr: (void *) pz_data); |
| 66 | return (char *) NULL; |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | space_left -= size_read; |
| 71 | space_used += size_read; |
| 72 | } while (! feof (fp)); |
| 73 | |
| 74 | pz_data = XRESIZEVEC (char, pz_data, space_used+1 ); |
| 75 | pz_data[ space_used ] = NUL; |
| 76 | |
| 77 | return pz_data; |
| 78 | } |
| 79 | |
| 80 | #ifdef IS_CXX_HEADER_NEEDED |
| 81 | t_bool |
| 82 | is_cxx_header (tCC* fname, tCC* text) |
| 83 | { |
| 84 | /* First, check to see if the file is in a C++ directory */ |
| 85 | for (;;) |
| 86 | { |
| 87 | switch (*(fname++)) |
| 88 | { |
| 89 | case 'C': /* check for "CC/" */ |
| 90 | if ((fname[0] == 'C') && (fname[1] == '/')) |
| 91 | return BOOL_TRUE; |
| 92 | break; |
| 93 | |
| 94 | case 'x': /* check for "xx/" */ |
| 95 | if ((fname[0] == 'x') && (fname[1] == '/')) |
| 96 | return BOOL_TRUE; |
| 97 | break; |
| 98 | |
| 99 | case '+': /* check for "++" */ |
| 100 | if (fname[0] == '+') |
| 101 | return BOOL_TRUE; |
| 102 | break; |
| 103 | |
| 104 | case NUL: |
| 105 | goto not_cxx_name; |
| 106 | } |
| 107 | } not_cxx_name:; |
| 108 | |
| 109 | /* Or it might contain one of several phrases which indicate C++ code. |
| 110 | Currently recognized are: |
| 111 | extern "C++" |
| 112 | -*- (Mode: )? C++ -*- (emacs mode marker) |
| 113 | template < |
| 114 | */ |
| 115 | { |
| 116 | tSCC cxxpat[] = "\ |
| 117 | extern[ \t]*\"C\\+\\+\"|\ |
| 118 | -\\*-[ \t]*([mM]ode:[ \t]*)?[cC]\\+\\+[; \t]*-\\*-|\ |
| 119 | template[ \t]*<|\ |
| 120 | ^[ \t]*class[ \t]|\ |
| 121 | (public|private|protected):|\ |
| 122 | ^[ \t]*#[ \t]*pragma[ \t]+(interface|implementation)\ |
| 123 | " ; |
| 124 | static regex_t cxxre; |
| 125 | static int compiled; |
| 126 | |
| 127 | if (!compiled) |
| 128 | compile_re (cxxpat, &cxxre, 0, "contents check" , "is_cxx_header" ); |
| 129 | |
| 130 | if (xregexec (&cxxre, text, 0, 0, 0) == 0) |
| 131 | return BOOL_TRUE; |
| 132 | } |
| 133 | |
| 134 | return BOOL_FALSE; |
| 135 | } |
| 136 | #endif /* CXX_TYPE_NEEDED */ |
| 137 | |
| 138 | #ifdef SKIP_QUOTE_NEEDED |
| 139 | /* |
| 140 | * Skip over a quoted string. Single quote strings may |
| 141 | * contain multiple characters if the first character is |
| 142 | * a backslash. Especially a backslash followed by octal digits. |
| 143 | * We are not doing a correctness syntax check here. |
| 144 | */ |
| 145 | tCC* |
| 146 | skip_quote(char q, char* text ) |
| 147 | { |
| 148 | for (;;) |
| 149 | { |
| 150 | char ch = *(text++); |
| 151 | switch (ch) |
| 152 | { |
| 153 | case '\\': |
| 154 | text++; /* skip over whatever character follows */ |
| 155 | break; |
| 156 | |
| 157 | case '"': |
| 158 | case '\'': |
| 159 | if (ch != q) |
| 160 | break; |
| 161 | /*FALLTHROUGH*/ |
| 162 | |
| 163 | case '\n': |
| 164 | case NUL: |
| 165 | goto skip_done; |
| 166 | } |
| 167 | } skip_done:; |
| 168 | |
| 169 | return text; |
| 170 | } |
| 171 | #endif /* SKIP_QUOTE_NEEDED */ |
| 172 | |
| 173 | /* * * * * * * * * * * * * |
| 174 | |
| 175 | Compile one regular expression pattern for later use. PAT contains |
| 176 | the pattern, RE points to a regex_t structure (which should have |
| 177 | been bzeroed). MATCH is 1 if we need to know where the regex |
| 178 | matched, 0 if not. If xregcomp fails, prints an error message and |
| 179 | aborts; E1 and E2 are strings to shove into the error message. |
| 180 | |
| 181 | The patterns we search for are all egrep patterns. |
| 182 | REG_EXTENDED|REG_NEWLINE produces identical regex syntax/semantics |
| 183 | to egrep (verified from 4.4BSD Programmer's Reference Manual). */ |
| 184 | void |
| 185 | compile_re( tCC* pat, regex_t* re, int match, tCC* e1, tCC* e2 ) |
| 186 | { |
| 187 | tSCC z_bad_comp[] = "fixincl ERROR: cannot compile %s regex for %s\n\ |
| 188 | \texpr = `%s'\n\terror %s\n" ; |
| 189 | int flags, err; |
| 190 | |
| 191 | flags = (match ? REG_EXTENDED|REG_NEWLINE |
| 192 | : REG_EXTENDED|REG_NEWLINE|REG_NOSUB); |
| 193 | err = xregcomp (preg: re, pattern: pat, cflags: flags); |
| 194 | |
| 195 | if (err) |
| 196 | { |
| 197 | char rerrbuf[1024]; |
| 198 | regerror (errcode: err, preg: re, errbuf: rerrbuf, errbuf_size: 1024); |
| 199 | fprintf (stderr, format: z_bad_comp, e1, e2, pat, rerrbuf); |
| 200 | exit (EXIT_FAILURE); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | /* * * * * * * * * * * * * |
| 205 | |
| 206 | Helper routine and data for the machine_name test and fix. */ |
| 207 | |
| 208 | tSCC mn_label_pat[] = "^[ \t]*#[ \t]*(if|ifdef|ifndef)[ \t]+" ; |
| 209 | static regex_t mn_label_re; |
| 210 | static regex_t mn_name_re; |
| 211 | |
| 212 | static int mn_compiled = 0; |
| 213 | |
| 214 | t_bool |
| 215 | mn_get_regexps(regex_t** label_re, regex_t** name_re, tCC* who ) |
| 216 | { |
| 217 | if (! pz_mn_name_pat) |
| 218 | return BOOL_FALSE; |
| 219 | |
| 220 | if (! mn_compiled) |
| 221 | { |
| 222 | compile_re (pat: mn_label_pat, re: &mn_label_re, match: 1, e1: "label pattern" , e2: who); |
| 223 | compile_re (pat: pz_mn_name_pat, re: &mn_name_re, match: 1, e1: "name pattern" , e2: who); |
| 224 | mn_compiled++; |
| 225 | } |
| 226 | *label_re = &mn_label_re; |
| 227 | *name_re = &mn_name_re; |
| 228 | return BOOL_TRUE; |
| 229 | } |
| 230 | |
| 231 | |
| 232 | #ifdef SEPARATE_FIX_PROC |
| 233 | |
| 234 | char* |
| 235 | make_raw_shell_str( char* pz_d, tCC* pz_s, size_t smax ) |
| 236 | { |
| 237 | tSCC zQ[] = "'\\''" ; |
| 238 | size_t dtaSize; |
| 239 | char* pz_d_start = pz_d; |
| 240 | |
| 241 | smax--; /* adjust for trailing NUL */ |
| 242 | |
| 243 | dtaSize = strlen( pz_s ) + 3; |
| 244 | |
| 245 | { |
| 246 | const char* pz = pz_s - 1; |
| 247 | |
| 248 | for (;;) { |
| 249 | pz = strchr( pz+1, '\'' ); |
| 250 | if (pz == (char*)NULL) |
| 251 | break; |
| 252 | dtaSize += sizeof( zQ )-1; |
| 253 | } |
| 254 | } |
| 255 | if (dtaSize > smax) |
| 256 | return (char*)NULL; |
| 257 | |
| 258 | *(pz_d++) = '\''; |
| 259 | |
| 260 | for (;;) { |
| 261 | if ((size_t) (pz_d - pz_d_start) >= smax) |
| 262 | return (char*)NULL; |
| 263 | switch (*(pz_d++) = *(pz_s++)) { |
| 264 | case NUL: |
| 265 | goto loopDone; |
| 266 | |
| 267 | case '\'': |
| 268 | if ((size_t) (pz_d - pz_d_start) >= smax - sizeof( zQ )-1) |
| 269 | return (char*)NULL; |
| 270 | strcpy( pz_d-1, zQ ); |
| 271 | pz_d += sizeof( zQ )-2; |
| 272 | } |
| 273 | } loopDone:; |
| 274 | pz_d[-1] = '\''; |
| 275 | *pz_d = NUL; |
| 276 | |
| 277 | return pz_d; |
| 278 | } |
| 279 | |
| 280 | #endif |
| 281 | |
| 282 | #if defined(__MINGW32__) |
| 283 | void |
| 284 | fix_path_separators (char* p) |
| 285 | { |
| 286 | while (p != NULL) |
| 287 | { |
| 288 | p = strchr (p, '\\'); |
| 289 | if (p != NULL) |
| 290 | { |
| 291 | *p = '/'; |
| 292 | ++p; |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | /* Count number of needle character ocurrences in str */ |
| 298 | static int |
| 299 | count_occurrences_of_char (char* str, char needle) |
| 300 | { |
| 301 | int cnt = 0; |
| 302 | |
| 303 | while (str) |
| 304 | { |
| 305 | str = strchr (str, needle); |
| 306 | if (str) |
| 307 | { |
| 308 | ++str; |
| 309 | ++cnt; |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | return cnt; |
| 314 | } |
| 315 | |
| 316 | /* On Mingw32, system function will just start cmd by default. |
| 317 | Call system function, but prepend ${CONFIG_SHELL} or ${SHELL} -c to the command, |
| 318 | replace newlines with '$'\n'', enclose command with double quotes |
| 319 | and escape special characters which were originally enclosed in single quotes. |
| 320 | */ |
| 321 | int |
| 322 | system_with_shell (char* s) |
| 323 | { |
| 324 | static const char z_shell_start_args[] = " -c \"" ; |
| 325 | static const char z_shell_end_args[] = "\"" ; |
| 326 | static const char z_shell_newline[] = "'$'\\n''" ; |
| 327 | |
| 328 | /* Use configured shell if present */ |
| 329 | char *env_shell = getenv ("CONFIG_SHELL" ); |
| 330 | int newline_cnt = count_occurrences_of_char (s, '\n'); |
| 331 | int escapes_cnt = count_occurrences_of_char( s, '\\') |
| 332 | + count_occurrences_of_char (s, '"') |
| 333 | + count_occurrences_of_char (s, '`'); |
| 334 | char *long_cmd; |
| 335 | char *cmd_endp; |
| 336 | int sys_result; |
| 337 | char *s_scan; |
| 338 | int in_quotes; |
| 339 | |
| 340 | if (env_shell == NULL) |
| 341 | env_shell = getenv ("SHELL" ); |
| 342 | |
| 343 | /* If neither CONFIGURED_SHELL nor SHELL is set, just call standard system function */ |
| 344 | if (env_shell == NULL) |
| 345 | return system (s); |
| 346 | |
| 347 | /* Allocate enough memory to fit newly created command string */ |
| 348 | long_cmd = XNEWVEC (char, strlen (env_shell) |
| 349 | + strlen (z_shell_start_args) |
| 350 | + strlen (s) |
| 351 | + newline_cnt * (strlen (z_shell_newline) - 1) |
| 352 | + escapes_cnt |
| 353 | + strlen (z_shell_end_args) |
| 354 | + 1); |
| 355 | |
| 356 | /* Start with ${SHELL} */ |
| 357 | strcpy (long_cmd, env_shell); |
| 358 | cmd_endp = long_cmd + strlen (long_cmd); |
| 359 | |
| 360 | /* Opening quote */ |
| 361 | strcpy (cmd_endp, z_shell_start_args); |
| 362 | cmd_endp += strlen (z_shell_start_args); |
| 363 | |
| 364 | /* Replace newlines and escape special chars */ |
| 365 | in_quotes = 0; |
| 366 | for (s_scan = s; *s_scan; ++s_scan) |
| 367 | { |
| 368 | switch (*s_scan) |
| 369 | { |
| 370 | case '\n': |
| 371 | if (in_quotes) |
| 372 | { |
| 373 | /* Replace newline inside quotes with '$'\n'' */ |
| 374 | strcpy (cmd_endp, z_shell_newline); |
| 375 | cmd_endp += strlen (z_shell_newline); |
| 376 | } |
| 377 | else |
| 378 | { |
| 379 | /* Replace newlines outside quotes with ; and merge subsequent newlines */ |
| 380 | *(cmd_endp++) = ';'; |
| 381 | *(cmd_endp++) = ' '; |
| 382 | while (*(s_scan + 1) == '\n' || *(s_scan + 1) == ' ' || *(s_scan + 1) == '\t') |
| 383 | ++s_scan; |
| 384 | } |
| 385 | break; |
| 386 | case '\'': |
| 387 | /* Escape single quote and toggle in_quotes flag */ |
| 388 | in_quotes = !in_quotes; |
| 389 | *(cmd_endp++) = *s_scan; |
| 390 | break; |
| 391 | case '\\': |
| 392 | case '`': |
| 393 | /* Escape backslash and backtick inside quotes */ |
| 394 | if (in_quotes) |
| 395 | *(cmd_endp++) = '\\'; |
| 396 | *(cmd_endp++) = *s_scan; |
| 397 | break; |
| 398 | case '"': |
| 399 | /* Escape double quotes always */ |
| 400 | *(cmd_endp++) = '\\'; |
| 401 | *(cmd_endp++) = *s_scan; |
| 402 | break; |
| 403 | default: |
| 404 | *(cmd_endp++) = *s_scan; |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | /* Closing quote */ |
| 409 | strcpy (cmd_endp, z_shell_end_args); |
| 410 | |
| 411 | sys_result = system (long_cmd); |
| 412 | |
| 413 | free (long_cmd); |
| 414 | |
| 415 | return sys_result; |
| 416 | } |
| 417 | |
| 418 | #endif /* defined(__MINGW32__) */ |
| 419 | |