1 2/* 3 4 Test to see if a particular fix should be applied to a header file. 5 6 Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. 7 8= = = = = = = = = = = = = = = = = = = = = = = = = 9 10NOTE TO DEVELOPERS 11 12The routines you write here must work closely with fixincl.c. 13 14Here are the rules: 15 161. Every test procedure name must be suffixed with "_fix". 17 These routines will be referenced from inclhack.def, sans the suffix. 18 192. Use the "FIX_PROC_HEAD()" macro _with_ the "_fix" suffix 20 (I cannot use the ## magic from ANSI C) for defining your entry point. 21 223. Put your test name into the FIXUP_TABLE. 23 244. Do not read anything from stdin. It is closed. 25 265. Write to stderr only in the event of a reportable error 27 In such an event, call "exit (EXIT_FAILURE)". 28 296. You have access to the fixDescList entry for the fix in question. 30 This may be useful, for example, if there are interesting strings 31 or pre-compiled regular expressions stored there. 32 33= = = = = = = = = = = = = = = = = = = = = = = = = 34 35This file is part of GNU CC. 36 37GNU CC is free software; you can redistribute it and/or modify 38it under the terms of the GNU General Public License as published by 39the Free Software Foundation; either version 2, or (at your option) 40any later version. 41 42GNU CC is distributed in the hope that it will be useful, 43but WITHOUT ANY WARRANTY; without even the implied warranty of 44MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 45GNU General Public License for more details. 46 47You should have received a copy of the GNU General Public License 48along with GNU CC; see the file COPYING. If not, write to 49the Free Software Foundation, 59 Temple Place - Suite 330, 50Boston, MA 02111-1307, USA. */ 51 52#include "fixlib.h" 53#define GTYPE_SE_CT 1 54 55#ifdef SEPARATE_FIX_PROC 56#include "fixincl.x" 57#endif 58 59tSCC zNeedsArg[] = "fixincl error: `%s' needs %s argument (c_fix_arg[%d])\n"; 60 61typedef void t_fix_proc PARAMS ((const char *, const char *, tFixDesc *)); 62typedef struct { 63 const char* fix_name; 64 t_fix_proc* fix_proc; 65} fix_entry_t; 66 67#define FIXUP_TABLE \ 68 _FT_( "char_macro_def", char_macro_def_fix ) \ 69 _FT_( "char_macro_use", char_macro_use_fix ) \ 70 _FT_( "format", format_fix ) \ 71 _FT_( "machine_name", machine_name_fix ) \ 72 _FT_( "wrap", wrap_fix ) \ 73 _FT_( "gnu_type", gnu_type_fix ) 74 75 76#define FIX_PROC_HEAD( fix ) \ 77static void fix PARAMS ((const char *, const char *, tFixDesc *)); /* avoid warning */ \ 78static void fix ( filname, text, p_fixd ) \ 79 const char* filname; \ 80 const char* text; \ 81 tFixDesc* p_fixd; 82 83#ifdef NEED_PRINT_QUOTE 84/* 85 * Skip over a quoted string. Single quote strings may 86 * contain multiple characters if the first character is 87 * a backslash. Especially a backslash followed by octal digits. 88 * We are not doing a correctness syntax check here. 89 */ 90static char* 91print_quote( q, text ) 92 char q; 93 char* text; 94{ 95 fputc( q, stdout ); 96 97 for (;;) 98 { 99 char ch = *(text++); 100 fputc( ch, stdout ); 101 102 switch (ch) 103 { 104 case '\\': 105 if (*text == NUL) 106 goto quote_done; 107 108 fputc( *(text++), stdout ); 109 break; 110 111 case '"': 112 case '\'': 113 if (ch != q) 114 break; 115 /*FALLTHROUGH*/ 116 117 case '\n': 118 case NUL: 119 goto quote_done; 120 } 121 } quote_done:; 122 123 return text; 124} 125#endif /* NEED_PRINT_QUOTE */ 126 127 128/* 129 * Emit the GNU standard type wrapped up in such a way that 130 * this thing can be encountered countless times during a compile 131 * and not cause even a warning. 132 */ 133static const char *emit_gnu_type PARAMS ((const char *, regmatch_t *)); 134static const char* 135emit_gnu_type ( text, rm ) 136 const char* text; 137 regmatch_t* rm; 138{ 139 char z_TYPE[ 64 ]; 140 char z_type[ 64 ]; 141 142 fwrite (text, rm[0].rm_so, 1, stdout); 143 144 { 145 const char* ps = text + rm[1].rm_so; 146 const char* pe = text + rm[1].rm_eo; 147 char* pd = z_type; 148 char* pD = z_TYPE; 149 150 while (ps < pe) 151 *(pD++) = TOUPPER( *(pd++) = *(ps++) ); 152 153 *pD = *pd = NUL; 154 } 155 156 /* 157 * Now print out the reformed typedef, 158 * with a C++ guard for WCHAR 159 */ 160 { 161 tSCC z_fmt[] = "\ 162#if !defined(_GCC_%s_T)%s\n\ 163#define _GCC_%s_T\n\ 164typedef __%s_TYPE__ %s_t;\n\ 165#endif\n"; 166 167 const char *const pz_guard = (strcmp (z_type, "wchar") == 0) 168 ? " && ! defined(__cplusplus)" : ""; 169 170 printf (z_fmt, z_TYPE, pz_guard, z_TYPE, z_TYPE, z_type); 171 } 172 173 return text += rm[0].rm_eo; 174} 175 176 177/* 178 * Copy the `format' string to std out, replacing `%n' expressions 179 * with the matched text from a regular expression evaluation. 180 * Doubled '%' characters will be replaced with a single copy. 181 * '%' characters in other contexts and all other characters are 182 * copied out verbatim. 183 */ 184static void format_write PARAMS ((tCC *, tCC *, regmatch_t[])); 185static void 186format_write (format, text, av) 187 tCC* format; 188 tCC* text; 189 regmatch_t av[]; 190{ 191 int c; 192 193 while ((c = (unsigned)*(format++)) != NUL) { 194 195 if (c != '%') 196 { 197 putchar(c); 198 continue; 199 } 200 201 c = (unsigned)*(format++); 202 203 /* 204 * IF the character following a '%' is not a digit, 205 * THEN we will always emit a '%' and we may or may 206 * not emit the following character. We will end on 207 * a NUL and we will emit only one of a pair of '%'. 208 */ 209 if (! ISDIGIT ( c )) 210 { 211 putchar( '%' ); 212 switch (c) { 213 case NUL: 214 return; 215 case '%': 216 break; 217 default: 218 putchar(c); 219 } 220 } 221 222 /* 223 * Emit the matched subexpression numbered 'c'. 224 * IF, of course, there was such a match... 225 */ 226 else { 227 regmatch_t* pRM = av + (c - (unsigned)'0'); 228 size_t len; 229 230 if (pRM->rm_so < 0) 231 continue; 232 233 len = pRM->rm_eo - pRM->rm_so; 234 if (len > 0) 235 fwrite(text + pRM->rm_so, len, 1, stdout); 236 } 237 } 238} 239 240 241/* 242 * Search for multiple copies of a regular expression. Each block 243 * of matched text is replaced with the format string, as described 244 * above in `format_write'. 245 */ 246FIX_PROC_HEAD( format_fix ) 247{ 248 tCC* pz_pat = p_fixd->patch_args[2]; 249 tCC* pz_fmt = p_fixd->patch_args[1]; 250 regex_t re; 251 regmatch_t rm[10]; 252 IGNORE_ARG(filname); 253 254 /* 255 * We must have a format 256 */ 257 if (pz_fmt == (tCC*)NULL) 258 { 259 fprintf( stderr, zNeedsArg, p_fixd->fix_name, "replacement format", 0 ); 260 exit (EXIT_BROKEN); 261 } 262 263 /* 264 * IF we don't have a search text, then go find the first 265 * regular expression among the tests. 266 */ 267 if (pz_pat == (tCC*)NULL) 268 { 269 tTestDesc* pTD = p_fixd->p_test_desc; 270 int ct = p_fixd->test_ct; 271 for (;;) 272 { 273 if (ct-- <= 0) 274 { 275 fprintf( stderr, zNeedsArg, p_fixd->fix_name, "search text", 1 ); 276 exit (EXIT_BROKEN); 277 } 278 279 if (pTD->type == TT_EGREP) 280 { 281 pz_pat = pTD->pz_test_text; 282 break; 283 } 284 285 pTD++; 286 } 287 } 288 289 /* 290 * Replace every copy of the text we find 291 */ 292 compile_re (pz_pat, &re, 1, "format search-text", "format_fix" ); 293 while (regexec (&re, text, 10, rm, 0) == 0) 294 { 295 fwrite( text, rm[0].rm_so, 1, stdout ); 296 format_write( pz_fmt, text, rm ); 297 text += rm[0].rm_eo; 298 } 299 300 /* 301 * Dump out the rest of the file 302 */ 303 fputs (text, stdout); 304} 305 306 307/* Scan the input file for all occurrences of text like this: 308 309 #define TIOCCONS _IO(T, 12) 310 311 and change them to read like this: 312 313 #define TIOCCONS _IO('T', 12) 314 315 which is the required syntax per the C standard. (The definition of 316 _IO also has to be tweaked - see below.) 'IO' is actually whatever you 317 provide as the `c_fix_arg' argument. */ 318 319FIX_PROC_HEAD( char_macro_use_fix ) 320{ 321 /* This regexp looks for a traditional-syntax #define (# in column 1) 322 of an object-like macro. */ 323 static const char pat[] = 324 "^#[ \t]*define[ \t]+[_A-Za-z][_A-Za-z0-9]*[ \t]+"; 325 static regex_t re; 326 327 const char* str = p_fixd->patch_args[1]; 328 regmatch_t rm[1]; 329 const char *p, *limit; 330 size_t len; 331 IGNORE_ARG(filname); 332 333 if (str == NULL) 334 { 335 fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0); 336 exit (EXIT_BROKEN); 337 } 338 339 len = strlen (str); 340 compile_re (pat, &re, 1, "macro pattern", "char_macro_use_fix"); 341 342 for (p = text; 343 regexec (&re, p, 1, rm, 0) == 0; 344 p = limit + 1) 345 { 346 /* p + rm[0].rm_eo is the first character of the macro replacement. 347 Find the end of the macro replacement, and the STR we were 348 sent to look for within the replacement. */ 349 p += rm[0].rm_eo; 350 limit = p - 1; 351 do 352 { 353 limit = strchr (limit + 1, '\n'); 354 if (!limit) 355 goto done; 356 } 357 while (limit[-1] == '\\'); 358 359 do 360 { 361 if (*p == str[0] && !strncmp (p+1, str+1, len-1)) 362 goto found; 363 } 364 while (++p < limit - len); 365 /* Hit end of line. */ 366 continue; 367 368 found: 369 /* Found STR on this line. If the macro needs fixing, 370 the next few chars will be whitespace or uppercase, 371 then an open paren, then a single letter. */ 372 while ((ISSPACE (*p) || ISUPPER (*p)) && p < limit) p++; 373 if (*p++ != '(') 374 continue; 375 if (!ISALPHA (*p)) 376 continue; 377 if (ISIDNUM (p[1])) 378 continue; 379 380 /* Splat all preceding text into the output buffer, 381 quote the character at p, then proceed. */ 382 fwrite (text, 1, p - text, stdout); 383 putchar ('\''); 384 putchar (*p); 385 putchar ('\''); 386 text = p + 1; 387 } 388 done: 389 fputs (text, stdout); 390} 391 392 393/* Scan the input file for all occurrences of text like this: 394 395 #define xxxIOxx(x, y) (....'x'<<16....) 396 397 and change them to read like this: 398 399 #define xxxIOxx(x, y) (....x<<16....) 400 401 which is the required syntax per the C standard. (The uses of _IO 402 also has to be tweaked - see above.) 'IO' is actually whatever 403 you provide as the `c_fix_arg' argument. */ 404FIX_PROC_HEAD( char_macro_def_fix ) 405{ 406 /* This regexp looks for any traditional-syntax #define (# in column 1). */ 407 static const char pat[] = 408 "^#[ \t]*define[ \t]+"; 409 static regex_t re; 410 411 const char* str = p_fixd->patch_args[1]; 412 regmatch_t rm[1]; 413 const char *p, *limit; 414 char arg; 415 size_t len; 416 IGNORE_ARG(filname); 417 418 if (str == NULL) 419 { 420 fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0); 421 exit (EXIT_BROKEN); 422 } 423 424 len = strlen (str); 425 compile_re (pat, &re, 1, "macro pattern", "fix_char_macro_defines"); 426 427 for (p = text; 428 regexec (&re, p, 1, rm, 0) == 0; 429 p = limit + 1) 430 { 431 /* p + rm[0].rm_eo is the first character of the macro name. 432 Find the end of the macro replacement, and the STR we were 433 sent to look for within the name. */ 434 p += rm[0].rm_eo; 435 limit = p - 1; 436 do 437 { 438 limit = strchr (limit + 1, '\n'); 439 if (!limit) 440 goto done; 441 } 442 while (limit[-1] == '\\'); 443 444 do 445 { 446 if (*p == str[0] && !strncmp (p+1, str+1, len-1)) 447 goto found; 448 p++; 449 } 450 while (ISIDNUM (*p)); 451 /* Hit end of macro name without finding the string. */ 452 continue; 453 454 found: 455 /* Found STR in this macro name. If the macro needs fixing, 456 there may be a few uppercase letters, then there will be an 457 open paren with _no_ intervening whitespace, and then a 458 single letter. */ 459 while (ISUPPER (*p) && p < limit) p++; 460 if (*p++ != '(') 461 continue; 462 if (!ISALPHA (*p)) 463 continue; 464 if (ISIDNUM (p[1])) 465 continue; 466 467 /* The character at P is the one to look for in the following 468 text. */ 469 arg = *p; 470 p += 2; 471 472 while (p < limit) 473 { 474 if (p[-1] == '\'' && p[0] == arg && p[1] == '\'') 475 { 476 /* Remove the quotes from this use of ARG. */ 477 p--; 478 fwrite (text, 1, p - text, stdout); 479 putchar (arg); 480 p += 3; 481 text = p; 482 } 483 else 484 p++; 485 } 486 } 487 done: 488 fputs (text, stdout); 489} 490 491/* Fix for machine name #ifdefs that are not in the namespace reserved 492 by the C standard. They won't be defined if compiling with -ansi, 493 and the headers will break. We go to some trouble to only change 494 #ifdefs where the macro is defined by GCC in non-ansi mode; this 495 minimizes the number of headers touched. */ 496 497#define SCRATCHSZ 64 /* hopefully long enough */ 498 499FIX_PROC_HEAD( machine_name_fix ) 500{ 501#ifndef MN_NAME_PAT 502 fputs( "The target machine has no needed machine name fixes\n", stderr ); 503#else 504 regmatch_t match[2]; 505 const char *line, *base, *limit, *p, *q; 506 regex_t *label_re, *name_re; 507 char scratch[SCRATCHSZ]; 508 size_t len; 509 IGNORE_ARG(filname); 510 IGNORE_ARG(p_fixd); 511 512 mn_get_regexps (&label_re, &name_re, "machine_name_fix"); 513 514 scratch[0] = '_'; 515 scratch[1] = '_'; 516 517 for (base = text; 518 regexec (label_re, base, 2, match, 0) == 0; 519 base = limit) 520 { 521 base += match[0].rm_eo; 522 /* We're looking at an #if or #ifdef. Scan forward for the 523 next non-escaped newline. */ 524 line = limit = base; 525 do 526 { 527 limit++; 528 limit = strchr (limit, '\n'); 529 if (!limit) 530 goto done; 531 } 532 while (limit[-1] == '\\'); 533 534 /* If the 'name_pat' matches in between base and limit, we have 535 a bogon. It is not worth the hassle of excluding comments 536 because comments on #if/#ifdef lines are rare, and strings on 537 such lines are illegal. 538 539 REG_NOTBOL means 'base' is not at the beginning of a line, which 540 shouldn't matter since the name_re has no ^ anchor, but let's 541 be accurate anyway. */ 542 543 for (;;) 544 { 545 again: 546 if (base == limit) 547 break; 548 549 if (regexec (name_re, base, 1, match, REG_NOTBOL)) 550 goto done; /* No remaining match in this file */ 551 552 /* Match; is it on the line? */ 553 if (match[0].rm_eo > limit - base) 554 break; 555 556 p = base + match[0].rm_so; 557 base += match[0].rm_eo; 558 559 /* One more test: if on the same line we have the same string 560 with the appropriate underscores, then leave it alone. 561 We want exactly two leading and trailing underscores. */ 562 if (*p == '_') 563 { 564 len = base - p - ((*base == '_') ? 2 : 1); 565 q = p + 1; 566 } 567 else 568 { 569 len = base - p - ((*base == '_') ? 1 : 0); 570 q = p; 571 } 572 if (len + 4 > SCRATCHSZ) 573 abort (); 574 memcpy (&scratch[2], q, len); 575 len += 2; 576 scratch[len++] = '_'; 577 scratch[len++] = '_'; 578 579 for (q = line; q <= limit - len; q++) 580 if (*q == '_' && !strncmp (q, scratch, len)) 581 goto again; 582 583 fwrite (text, 1, p - text, stdout); 584 fwrite (scratch, 1, len, stdout); 585 586 text = base; 587 } 588 } 589 done: 590#endif 591 fputs (text, stdout); 592} 593 594 595FIX_PROC_HEAD( wrap_fix ) 596{ 597 tSCC z_no_wrap_pat[] = "^#if.*__need_"; 598 static regex_t no_wrapping_re; /* assume zeroed data */ 599 600 tCC* pz_name = NULL; 601 602 if (no_wrapping_re.allocated == 0) 603 compile_re( z_no_wrap_pat, &no_wrapping_re, 0, "no-wrap pattern", 604 "wrap-fix" ); 605 606 /* 607 * IF we do *not* match the no-wrap re, then we have a double negative. 608 * A double negative means YES. 609 */ 610 if (regexec( &no_wrapping_re, text, 0, NULL, 0 ) != 0) 611 { 612 /* 613 * A single file can get wrapped more than once by different fixes. 614 * A single fix can wrap multiple files. Therefore, guard with 615 * *both* the fix name and the file name. 616 */ 617 size_t ln = strlen( filname ) + strlen( p_fixd->fix_name ) + 14; 618 char* pz = xmalloc( ln ); 619 pz_name = pz; 620 sprintf( pz, "FIXINC_WRAP_%s-%s", filname, p_fixd->fix_name ); 621 622 for (pz += 12; 1; pz++) { 623 char ch = *pz; 624 625 if (ch == NUL) 626 break; 627 628 if (! ISALNUM( ch )) { 629 *pz = '_'; 630 } 631 else { 632 *pz = TOUPPER( ch ); 633 } 634 } 635 636 printf( "#ifndef %s\n", pz_name ); 637 printf( "#define %s 1\n\n", pz_name ); 638 } 639 640 if (p_fixd->patch_args[1] == (tCC*)NULL) 641 fputs( text, stdout ); 642 643 else { 644 fputs( p_fixd->patch_args[1], stdout ); 645 fputs( text, stdout ); 646 if (p_fixd->patch_args[2] != (tCC*)NULL) 647 fputs( p_fixd->patch_args[2], stdout ); 648 } 649 650 if (pz_name != NULL) { 651 printf( "\n#endif /* %s */\n", pz_name ); 652 free( (void*)pz_name ); 653 } 654} 655 656 657/* 658 * Search for multiple copies of a regular expression. Each block 659 * of matched text is replaced with the format string, as described 660 * above in `format_write'. 661 */ 662FIX_PROC_HEAD( gnu_type_fix ) 663{ 664 const char* pz_pat; 665 regex_t re; 666 regmatch_t rm[GTYPE_SE_CT+1]; 667 IGNORE_ARG(filname); 668 669 { 670 tTestDesc* pTD = p_fixd->p_test_desc; 671 int ct = p_fixd->test_ct; 672 for (;;) 673 { 674 if (ct-- <= 0) 675 { 676 fprintf (stderr, zNeedsArg, p_fixd->fix_name, "search text", 1); 677 exit (EXIT_BROKEN); 678 } 679 680 if (pTD->type == TT_EGREP) 681 { 682 pz_pat = pTD->pz_test_text; 683 break; 684 } 685 686 pTD++; 687 } 688 } 689 690 compile_re (pz_pat, &re, 1, "gnu type typedef", "gnu_type_fix"); 691 692 while (regexec (&re, text, GTYPE_SE_CT+1, rm, 0) == 0) 693 { 694 text = emit_gnu_type (text, rm); 695 } 696 697 /* 698 * Dump out the rest of the file 699 */ 700 fputs (text, stdout); 701} 702 703 704/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 705 706 test for fix selector 707 708 THIS IS THE ONLY EXPORTED ROUTINE 709 710*/ 711void 712apply_fix( p_fixd, filname ) 713 tFixDesc* p_fixd; 714 tCC* filname; 715{ 716#define _FT_(n,p) { n, p }, 717 static fix_entry_t fix_table[] = { FIXUP_TABLE { NULL, NULL }}; 718#undef _FT_ 719#define FIX_TABLE_CT (ARRAY_SIZE (fix_table)-1) 720 721 tCC* fixname = p_fixd->patch_args[0]; 722 char* buf; 723 int ct = FIX_TABLE_CT; 724 fix_entry_t* pfe = fix_table; 725 726 for (;;) 727 { 728 if (strcmp (pfe->fix_name, fixname) == 0) 729 break; 730 if (--ct <= 0) 731 { 732 fprintf (stderr, "fixincl error: the `%s' fix is unknown\n", 733 fixname ); 734 exit (EXIT_BROKEN); 735 } 736 pfe++; 737 } 738 739 buf = load_file_data (stdin); 740 (*pfe->fix_proc)( filname, buf, p_fixd ); 741} 742 743#ifdef SEPARATE_FIX_PROC 744tSCC z_usage[] = 745"USAGE: applyfix <fix-name> <file-to-fix> <file-source> <file-destination>\n"; 746tSCC z_reopen[] = 747"FS error %d (%s) reopening %s as std%s\n"; 748 749int 750main( argc, argv ) 751 int argc; 752 char** argv; 753{ 754 tFixDesc* pFix; 755 char* pz_tmptmp; 756 char* pz_tmp_base; 757 char* pz_tmp_dot; 758 759 if (argc != 5) 760 { 761 usage_failure: 762 fputs (z_usage, stderr); 763 return EXIT_FAILURE; 764 } 765 766 { 767 char* pz = argv[1]; 768 long idx; 769 770 if (! ISDIGIT ( *pz )) 771 goto usage_failure; 772 773 idx = strtol (pz, &pz, 10); 774 if ((*pz != NUL) || ((unsigned)idx >= FIX_COUNT)) 775 goto usage_failure; 776 pFix = fixDescList + idx; 777 } 778 779 if (freopen (argv[3], "r", stdin) != stdin) 780 { 781 fprintf (stderr, z_reopen, errno, strerror( errno ), argv[3], "in"); 782 return EXIT_FAILURE; 783 } 784 785 pz_tmptmp = (char*)xmalloc( strlen( argv[4] ) + 5 ); 786 strcpy( pz_tmptmp, argv[4] ); 787 788 /* Don't lose because "12345678" and "12345678X" map to the same 789 file under DOS restricted 8+3 file namespace. Note that DOS 790 doesn't allow more than one dot in the trunk of a file name. */ 791 pz_tmp_base = basename( pz_tmptmp ); 792 pz_tmp_dot = strchr( pz_tmp_base, '.' ); 793 if (pathconf( pz_tmptmp, _PC_NAME_MAX ) <= 12 /* is this DOS or Windows9X? */ 794 && pz_tmp_dot != (char*)NULL) 795 strcpy (pz_tmp_dot+1, "X"); /* nuke the original extension */ 796 else 797 strcat (pz_tmptmp, ".X"); 798 if (freopen (pz_tmptmp, "w", stdout) != stdout) 799 { 800 fprintf (stderr, z_reopen, errno, strerror( errno ), pz_tmptmp, "out"); 801 return EXIT_FAILURE; 802 } 803 804 apply_fix (pFix, argv[1]); 805 fclose (stdout); 806 fclose (stdin); 807 unlink (argv[4]); 808 if (rename (pz_tmptmp, argv[4]) != 0) 809 { 810 fprintf (stderr, "error %d (%s) renaming %s to %s\n", errno, 811 strerror( errno ), pz_tmptmp, argv[4]); 812 return EXIT_FAILURE; 813 } 814 815 return EXIT_SUCCESS; 816} 817#endif 818