1/* fix-header.c - Make C header file suitable for C++. 2 Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 3 1999, 2000, 2001 Free Software Foundation, Inc. 4 5This program is free software; you can redistribute it and/or modify it 6under the terms of the GNU General Public License as published by the 7Free Software Foundation; either version 2, or (at your option) any 8later version. 9 10This program is distributed in the hope that it will be useful, 11but WITHOUT ANY WARRANTY; without even the implied warranty of 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 18 19/* This program massages a system include file (such as stdio.h), 20 into a form that is compatible with GNU C and GNU C++. 21 22 * extern "C" { ... } braces are added (inside #ifndef __cplusplus), 23 if they seem to be needed. These prevent C++ compilers from name 24 mangling the functions inside the braces. 25 26 * If an old-style incomplete function declaration is seen (without 27 an argument list), and it is a "standard" function listed in 28 the file sys-protos.h (and with a non-empty argument list), then 29 the declaration is converted to a complete prototype by replacing 30 the empty parameter list with the argument list from sys-protos.h. 31 32 * The program can be given a list of (names of) required standard 33 functions (such as fclose for stdio.h). If a required function 34 is not seen in the input, then a prototype for it will be 35 written to the output. 36 37 * If all of the non-comment code of the original file is protected 38 against multiple inclusion: 39 #ifndef FOO 40 #define FOO 41 <body of include file> 42 #endif 43 then extra matter added to the include file is placed inside the <body>. 44 45 * If the input file is OK (nothing needs to be done); 46 the output file is not written (nor removed if it exists). 47 48 There are also some special actions that are done for certain 49 well-known standard include files: 50 51 * If argv[1] is "sys/stat.h", the Posix.1 macros 52 S_ISBLK, S_ISCHR, S_ISDIR, S_ISFIFO, S_ISLNK, S_ISREG are added if 53 they were missing, and the corresponding "traditional" S_IFxxx 54 macros were defined. 55 56 * If argv[1] is "errno.h", errno is declared if it was missing. 57 58 * TODO: The input file should be read complete into memory, because: 59 a) it needs to be scanned twice anyway, and 60 b) it would be nice to allow update in place. 61 62 Usage: 63 fix-header FOO.H INFILE.H OUTFILE.H [OPTIONS] 64 where: 65 * FOO.H is the relative file name of the include file, 66 as it would be #include'd by a C file. (E.g. stdio.h) 67 * INFILE.H is a full pathname for the input file (e.g. /usr/include/stdio.h) 68 * OUTFILE.H is the full pathname for where to write the output file, 69 if anything needs to be done. (e.g. ./include/stdio.h) 70 * OPTIONS are such as you would pass to cpp. 71 72 Written by Per Bothner <bothner@cygnus.com>, July 1993. */ 73 74#include "hconfig.h" 75#include "system.h" 76#include "obstack.h" 77#include "scan.h" 78#include "cpplib.h" 79 80static void v_fatal PARAMS ((const char *, va_list)) ATTRIBUTE_PRINTF (1,0) ATTRIBUTE_NORETURN; 81static void fatal PARAMS ((const char *, ...)) ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN; 82 83sstring buf; 84 85int verbose = 0; 86int partial_count = 0; 87int warnings = 0; 88 89/* We no longer need to add extern "C", because cpp implicitly 90 forces the standard include files to be treated as C. */ 91/*#define ADD_MISSING_EXTERN_C 1 */ 92 93#if ADD_MISSING_EXTERN_C 94int missing_extern_C_count = 0; 95#endif 96 97#include "xsys-protos.h" 98 99#ifdef FIXPROTO_IGNORE_LIST 100/* This is a currently unused feature. */ 101 102/* List of files and directories to ignore. 103 A directory name (ending in '/') means ignore anything in that 104 directory. (It might be more efficient to do directory pruning 105 earlier in fixproto, but this is simpler and easier to customize.) */ 106 107static const char *const files_to_ignore[] = { 108 "X11/", 109 FIXPROTO_IGNORE_LIST 110 0 111}; 112#endif 113 114char *inf_buffer; 115char *inf_limit; 116char *inf_ptr; 117static const char *cur_file; 118 119/* Certain standard files get extra treatment */ 120 121enum special_file 122{ 123 no_special, 124#ifdef errno_h 125#undef errno_h 126#endif 127 errno_h, 128#ifdef stdio_h 129#undef stdio_h 130#endif 131 stdio_h, 132#ifdef stdlib_h 133#undef stdlib_h 134#endif 135 stdlib_h, 136#ifdef sys_stat_h 137#undef sys_stat_h 138#endif 139 sys_stat_h 140}; 141 142/* A NAMELIST is a sequence of names, separated by '\0', and terminated 143 by an empty name (i.e. by "\0\0"). */ 144 145typedef const char *namelist; 146 147/* The following macros provide the bits for symbol_flags. */ 148typedef int symbol_flags; 149 150/* Used to mark names defined in the ANSI/ISO C standard. */ 151#define ANSI_SYMBOL 1 152 153/* We no longer massage include files for POSIX or XOPEN symbols, 154 as there are now several versions of the POSIX and XOPEN standards, 155 and it would be a maintenance nightmare for us to track them all. 156 Better to be compatible with the system include files. */ 157/*#define ADD_MISSING_POSIX 1 */ 158/*#define ADD_MISSING_XOPEN 1 */ 159 160#if ADD_MISSING_POSIX 161/* Used to mark names defined in the Posix.1 or Posix.2 standard. */ 162#define POSIX1_SYMBOL 2 163#define POSIX2_SYMBOL 4 164#else 165#define POSIX1_SYMBOL 0 166#define POSIX2_SYMBOL 0 167#endif 168 169#if ADD_MISSING_XOPEN 170/* Used to mark names defined in X/Open Portability Guide. */ 171#define XOPEN_SYMBOL 8 172/* Used to mark names defined in X/Open UNIX Extensions. */ 173#define XOPEN_EXTENDED_SYMBOL 16 174#else 175#define XOPEN_SYMBOL 0 176#define XOPEN_EXTENDED_SYMBOL 0 177#endif 178 179/* Used to indicate names that are not functions */ 180#define MACRO_SYMBOL 512 181 182struct symbol_list { 183 symbol_flags flags; 184 namelist names; 185}; 186 187#define SYMBOL_TABLE_SIZE 10 188struct symbol_list symbol_table[SYMBOL_TABLE_SIZE]; 189int cur_symbol_table_size; 190 191static void add_symbols PARAMS ((symbol_flags, namelist)); 192static struct fn_decl *lookup_std_proto PARAMS ((const char *, int)); 193static void write_lbrac PARAMS ((void)); 194static void recognized_macro PARAMS ((const char *)); 195static void check_macro_names PARAMS ((cpp_reader *, namelist)); 196static void read_scan_file PARAMS ((char *, int, char **)); 197static void write_rbrac PARAMS ((void)); 198static int inf_skip_spaces PARAMS ((int)); 199static int inf_read_upto PARAMS ((sstring *, int)); 200static int inf_scan_ident PARAMS ((sstring *, int)); 201static int check_protection PARAMS ((int *, int *)); 202static void cb_file_change PARAMS ((cpp_reader *, const struct line_map *)); 203 204static void 205add_symbols (flags, names) 206 symbol_flags flags; 207 namelist names; 208{ 209 symbol_table[cur_symbol_table_size].flags = flags; 210 symbol_table[cur_symbol_table_size].names = names; 211 cur_symbol_table_size++; 212 if (cur_symbol_table_size >= SYMBOL_TABLE_SIZE) 213 fatal ("too many calls to add_symbols"); 214 symbol_table[cur_symbol_table_size].names = NULL; /* Termination. */ 215} 216 217struct std_include_entry { 218 const char *const name; 219 const symbol_flags flags; 220 const namelist names; 221}; 222 223const char NONE[] = ""; /* The empty namelist. */ 224 225/* Special name to indicate a continuation line in std_include_table. */ 226const char CONTINUED[] = ""; 227 228const struct std_include_entry *include_entry; 229 230const struct std_include_entry std_include_table [] = { 231 { "ctype.h", ANSI_SYMBOL, 232 "isalnum\0isalpha\0iscntrl\0isdigit\0isgraph\0islower\0\ 233isprint\0ispunct\0isspace\0isupper\0isxdigit\0tolower\0toupper\0" }, 234 235 { "dirent.h", POSIX1_SYMBOL, "closedir\0opendir\0readdir\0rewinddir\0"}, 236 237 { "errno.h", ANSI_SYMBOL|MACRO_SYMBOL, "errno\0" }, 238 239 /* ANSI_SYMBOL is wrong, but ... */ 240 { "curses.h", ANSI_SYMBOL, "box\0delwin\0endwin\0getcurx\0getcury\0initscr\0\ 241mvcur\0mvwprintw\0mvwscanw\0newwin\0overlay\0overwrite\0\ 242scroll\0subwin\0touchwin\0waddstr\0wclear\0wclrtobot\0wclrtoeol\0\ 243waddch\0wdelch\0wdeleteln\0werase\0wgetch\0wgetstr\0winsch\0winsertln\0\ 244wmove\0wprintw\0wrefresh\0wscanw\0wstandend\0wstandout\0" }, 245 246 { "fcntl.h", POSIX1_SYMBOL, "creat\0fcntl\0open\0" }, 247 248 /* Maybe also "getgrent fgetgrent setgrent endgrent" */ 249 { "grp.h", POSIX1_SYMBOL, "getgrgid\0getgrnam\0" }, 250 251/*{ "limit.h", ... provided by gcc }, */ 252 253 { "locale.h", ANSI_SYMBOL, "localeconv\0setlocale\0" }, 254 255 { "math.h", ANSI_SYMBOL, 256 "acos\0asin\0atan\0atan2\0ceil\0cos\0cosh\0exp\0\ 257fabs\0floor\0fmod\0frexp\0ldexp\0log10\0log\0modf\0pow\0sin\0sinh\0sqrt\0\ 258tan\0tanh\0" }, 259 260 { CONTINUED, ANSI_SYMBOL|MACRO_SYMBOL, "HUGE_VAL\0" }, 261 262 { "pwd.h", POSIX1_SYMBOL, "getpwnam\0getpwuid\0" }, 263 264 /* Left out siglongjmp sigsetjmp - these depend on sigjmp_buf. */ 265 { "setjmp.h", ANSI_SYMBOL, "longjmp\0setjmp\0" }, 266 267 /* Left out signal() - its prototype is too complex for us! 268 Also left out "sigaction sigaddset sigdelset sigemptyset 269 sigfillset sigismember sigpending sigprocmask sigsuspend" 270 because these need sigset_t or struct sigaction. 271 Most systems that provide them will also declare them. */ 272 { "signal.h", ANSI_SYMBOL, "kill\0raise\0" }, 273 274 { "stdio.h", ANSI_SYMBOL, 275 "clearerr\0fclose\0feof\0ferror\0fflush\0fgetc\0fgetpos\0\ 276fgets\0fopen\0fprintf\0fputc\0fputs\0fread\0freopen\0fscanf\0fseek\0\ 277fsetpos\0ftell\0fwrite\0getc\0getchar\0gets\0perror\0\ 278printf\0putc\0putchar\0puts\0remove\0rename\0rewind\0scanf\0setbuf\0\ 279setvbuf\0sprintf\0sscanf\0vprintf\0vsprintf\0vfprintf\0tmpfile\0\ 280tmpnam\0ungetc\0" }, 281 { CONTINUED, POSIX1_SYMBOL, "fdopen\0fileno\0" }, 282 { CONTINUED, POSIX2_SYMBOL, "pclose\0popen\0" }, /* I think ... */ 283/* Should perhaps also handle NULL, EOF, ... ? */ 284 285 /* "div ldiv", - ignored because these depend on div_t, ldiv_t 286 ignore these: "mblen mbstowcs mbstowc wcstombs wctomb" 287 Left out getgroups, because SunOS4 has incompatible BSD and SVR4 versions. 288 Should perhaps also add NULL */ 289 { "stdlib.h", ANSI_SYMBOL, 290 "abort\0abs\0atexit\0atof\0atoi\0atol\0bsearch\0calloc\0\ 291exit\0free\0getenv\0labs\0malloc\0putenv\0qsort\0rand\0realloc\0\ 292srand\0strtod\0strtol\0strtoul\0system\0" }, 293 { CONTINUED, ANSI_SYMBOL|MACRO_SYMBOL, "EXIT_FAILURE\0EXIT_SUCCESS\0" }, 294 295 { "string.h", ANSI_SYMBOL, "memchr\0memcmp\0memcpy\0memmove\0memset\0\ 296strcat\0strchr\0strcmp\0strcoll\0strcpy\0strcspn\0strerror\0\ 297strlen\0strncat\0strncmp\0strncpy\0strpbrk\0strrchr\0strspn\0strstr\0\ 298strtok\0strxfrm\0" }, 299/* Should perhaps also add NULL and size_t */ 300 301 { "strings.h", XOPEN_EXTENDED_SYMBOL, 302 "bcmp\0bcopy\0bzero\0ffs\0index\0rindex\0strcasecmp\0strncasecmp\0" }, 303 304 { "strops.h", XOPEN_EXTENDED_SYMBOL, "ioctl\0" }, 305 306 /* Actually, XPG4 does not seem to have <sys/ioctl.h>, but defines 307 ioctl in <strops.h>. However, many systems have it is sys/ioctl.h, 308 and many systems do have <sys/ioctl.h> but not <strops.h>. */ 309 { "sys/ioctl.h", XOPEN_EXTENDED_SYMBOL, "ioctl\0" }, 310 311 { "sys/socket.h", XOPEN_EXTENDED_SYMBOL, "socket\0" }, 312 313 { "sys/stat.h", POSIX1_SYMBOL, 314 "chmod\0fstat\0mkdir\0mkfifo\0stat\0lstat\0umask\0" }, 315 { CONTINUED, POSIX1_SYMBOL|MACRO_SYMBOL, 316 "S_ISDIR\0S_ISBLK\0S_ISCHR\0S_ISFIFO\0S_ISREG\0S_ISLNK\0S_IFDIR\0\ 317S_IFBLK\0S_IFCHR\0S_IFIFO\0S_IFREG\0S_IFLNK\0" }, 318 { CONTINUED, XOPEN_EXTENDED_SYMBOL, "fchmod\0" }, 319 320#if 0 321/* How do we handle fd_set? */ 322 { "sys/time.h", XOPEN_EXTENDED_SYMBOL, "select\0" }, 323 { "sys/select.h", XOPEN_EXTENDED_SYMBOL /* fake */, "select\0" }, 324#endif 325 326 { "sys/times.h", POSIX1_SYMBOL, "times\0" }, 327 /* "sys/types.h" add types (not in old g++-include) */ 328 329 { "sys/utsname.h", POSIX1_SYMBOL, "uname\0" }, 330 331 { "sys/wait.h", POSIX1_SYMBOL, "wait\0waitpid\0" }, 332 { CONTINUED, POSIX1_SYMBOL|MACRO_SYMBOL, 333 "WEXITSTATUS\0WIFEXITED\0WIFSIGNALED\0WIFSTOPPED\0WSTOPSIG\0\ 334WTERMSIG\0WNOHANG\0WNOTRACED\0" }, 335 336 { "tar.h", POSIX1_SYMBOL, NONE }, 337 338 { "termios.h", POSIX1_SYMBOL, 339 "cfgetispeed\0cfgetospeed\0cfsetispeed\0cfsetospeed\0tcdrain\0tcflow\0tcflush\0tcgetattr\0tcsendbreak\0tcsetattr\0" }, 340 341 { "time.h", ANSI_SYMBOL, 342 "asctime\0clock\0ctime\0difftime\0gmtime\0localtime\0mktime\0strftime\0time\0tzset\0" }, 343 344 { "unistd.h", POSIX1_SYMBOL, 345 "_exit\0access\0alarm\0chdir\0chown\0close\0ctermid\0cuserid\0\ 346dup\0dup2\0execl\0execle\0execlp\0execv\0execve\0execvp\0fork\0fpathconf\0\ 347getcwd\0getegid\0geteuid\0getgid\0getlogin\0getpgrp\0getpid\0\ 348getppid\0getuid\0isatty\0link\0lseek\0pathconf\0pause\0pipe\0read\0rmdir\0\ 349setgid\0setpgid\0setsid\0setuid\0sleep\0sysconf\0tcgetpgrp\0tcsetpgrp\0\ 350ttyname\0unlink\0write\0" }, 351 { CONTINUED, POSIX2_SYMBOL, "getopt\0" }, 352 { CONTINUED, XOPEN_EXTENDED_SYMBOL, 353 "lockf\0gethostid\0gethostname\0readlink\0symlink\0" }, 354 355 { "utime.h", POSIX1_SYMBOL, "utime\0" }, 356 357 { NULL, 0, NONE } 358}; 359 360enum special_file special_file_handling = no_special; 361 362/* They are set if the corresponding macro has been seen. */ 363/* The following are only used when handling sys/stat.h */ 364int seen_S_IFBLK = 0, seen_S_ISBLK = 0; 365int seen_S_IFCHR = 0, seen_S_ISCHR = 0; 366int seen_S_IFDIR = 0, seen_S_ISDIR = 0; 367int seen_S_IFIFO = 0, seen_S_ISFIFO = 0; 368int seen_S_IFLNK = 0, seen_S_ISLNK = 0; 369int seen_S_IFREG = 0, seen_S_ISREG = 0; 370/* The following are only used when handling errno.h */ 371int seen_errno = 0; 372/* The following are only used when handling stdlib.h */ 373int seen_EXIT_FAILURE = 0, seen_EXIT_SUCCESS = 0; 374 375struct obstack scan_file_obstack; 376 377/* NOTE: If you edit this, also edit gen-protos.c !! */ 378 379static struct fn_decl * 380lookup_std_proto (name, name_length) 381 const char *name; 382 int name_length; 383{ 384 int i = hashstr (name, name_length) % HASH_SIZE; 385 int i0 = i; 386 for (;;) 387 { 388 struct fn_decl *fn; 389 if (hash_tab[i] == 0) 390 return NULL; 391 fn = &std_protos[hash_tab[i]]; 392 if ((int) strlen (fn->fname) == name_length 393 && strncmp (fn->fname, name, name_length) == 0) 394 return fn; 395 i = (i+1) % HASH_SIZE; 396 if (i == i0) 397 abort (); 398 } 399} 400 401char *inc_filename; 402int inc_filename_length; 403const char *progname = "fix-header"; 404FILE *outf; 405sstring line; 406 407int lbrac_line, rbrac_line; 408 409int required_unseen_count = 0; 410int required_other = 0; 411 412static void 413write_lbrac () 414{ 415 416#if ADD_MISSING_EXTERN_C 417 if (missing_extern_C_count + required_unseen_count > 0) 418 fprintf (outf, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"); 419#endif 420 421 if (partial_count) 422 { 423 fprintf (outf, "#ifndef _PARAMS\n"); 424 fprintf (outf, "#if defined(__STDC__) || defined(__cplusplus)\n"); 425 fprintf (outf, "#define _PARAMS(ARGS) ARGS\n"); 426 fprintf (outf, "#else\n"); 427 fprintf (outf, "#define _PARAMS(ARGS) ()\n"); 428 fprintf (outf, "#endif\n#endif /* _PARAMS */\n"); 429 } 430} 431 432struct partial_proto 433{ 434 struct partial_proto *next; 435 struct fn_decl *fn; 436 int line_seen; 437}; 438 439struct partial_proto *partial_proto_list = NULL; 440 441struct partial_proto required_dummy_proto, seen_dummy_proto; 442#define REQUIRED(FN) ((FN)->partial == &required_dummy_proto) 443#define SET_REQUIRED(FN) ((FN)->partial = &required_dummy_proto) 444#define SET_SEEN(FN) ((FN)->partial = &seen_dummy_proto) 445#define SEEN(FN) ((FN)->partial == &seen_dummy_proto) 446 447static void 448recognized_macro (fname) 449 const char *fname; 450{ 451 /* The original include file defines fname as a macro. */ 452 struct fn_decl *fn = lookup_std_proto (fname, strlen (fname)); 453 454 /* Since fname is a macro, don't require a prototype for it. */ 455 if (fn) 456 { 457 if (REQUIRED (fn)) 458 required_unseen_count--; 459 SET_SEEN (fn); 460 } 461 462 switch (special_file_handling) 463 { 464 case errno_h: 465 if (strcmp (fname, "errno") == 0 && !seen_errno) 466 seen_errno = 1, required_other--; 467 break; 468 case stdlib_h: 469 if (strcmp (fname, "EXIT_FAILURE") == 0 && !seen_EXIT_FAILURE) 470 seen_EXIT_FAILURE = 1, required_other--; 471 if (strcmp (fname, "EXIT_SUCCESS") == 0 && !seen_EXIT_SUCCESS) 472 seen_EXIT_SUCCESS = 1, required_other--; 473 break; 474 case sys_stat_h: 475 if (fname[0] == 'S' && fname[1] == '_') 476 { 477 if (strcmp (fname, "S_IFBLK") == 0) seen_S_IFBLK++; 478 else if (strcmp (fname, "S_ISBLK") == 0) seen_S_ISBLK++; 479 else if (strcmp (fname, "S_IFCHR") == 0) seen_S_IFCHR++; 480 else if (strcmp (fname, "S_ISCHR") == 0) seen_S_ISCHR++; 481 else if (strcmp (fname, "S_IFDIR") == 0) seen_S_IFDIR++; 482 else if (strcmp (fname, "S_ISDIR") == 0) seen_S_ISDIR++; 483 else if (strcmp (fname, "S_IFIFO") == 0) seen_S_IFIFO++; 484 else if (strcmp (fname, "S_ISFIFO") == 0) seen_S_ISFIFO++; 485 else if (strcmp (fname, "S_IFLNK") == 0) seen_S_IFLNK++; 486 else if (strcmp (fname, "S_ISLNK") == 0) seen_S_ISLNK++; 487 else if (strcmp (fname, "S_IFREG") == 0) seen_S_IFREG++; 488 else if (strcmp (fname, "S_ISREG") == 0) seen_S_ISREG++; 489 } 490 break; 491 492 default: 493 break; 494 } 495} 496 497void 498recognized_extern (name) 499 const cpp_token *name; 500{ 501 switch (special_file_handling) 502 { 503 case errno_h: 504 if (cpp_ideq (name, "errno")) 505 seen_errno = 1, required_other--; 506 break; 507 508 default: 509 break; 510 } 511} 512 513/* Called by scan_decls if it saw a function definition for a function 514 named FNAME. KIND is 'I' for an inline function; 'F' if a normal 515 function declaration preceded by 'extern "C"' (or nested inside 516 'extern "C"' braces); or 'f' for other function declarations. */ 517 518void 519recognized_function (fname, line, kind, have_arg_list) 520 const cpp_token *fname; 521 unsigned int line; 522 int kind; /* One of 'f' 'F' or 'I' */ 523 int have_arg_list; 524{ 525 struct partial_proto *partial; 526 int i; 527 struct fn_decl *fn; 528#if ADD_MISSING_EXTERN_C 529 if (kind == 'f') 530 missing_extern_C_count++; 531#endif 532 533 fn = lookup_std_proto ((const char *) NODE_NAME (fname->val.node), 534 NODE_LEN (fname->val.node)); 535 536 /* Remove the function from the list of required function. */ 537 if (fn) 538 { 539 if (REQUIRED (fn)) 540 required_unseen_count--; 541 SET_SEEN (fn); 542 } 543 544 /* If we have a full prototype, we're done. */ 545 if (have_arg_list) 546 return; 547 548 if (kind == 'I') /* don't edit inline function */ 549 return; 550 551 /* If the partial prototype was included from some other file, 552 we don't need to patch it up (in this run). */ 553 i = strlen (cur_file); 554 if (i < inc_filename_length 555 || strcmp (inc_filename, cur_file + (i - inc_filename_length)) != 0) 556 return; 557 558 if (fn == NULL) 559 return; 560 if (fn->params[0] == '\0') 561 return; 562 563 /* We only have a partial function declaration, 564 so remember that we have to add a complete prototype. */ 565 partial_count++; 566 partial = (struct partial_proto *) 567 obstack_alloc (&scan_file_obstack, sizeof (struct partial_proto)); 568 partial->line_seen = line; 569 partial->fn = fn; 570 fn->partial = partial; 571 partial->next = partial_proto_list; 572 partial_proto_list = partial; 573 if (verbose) 574 { 575 fprintf (stderr, "(%s: %s non-prototype function declaration.)\n", 576 inc_filename, fn->fname); 577 } 578} 579 580/* For any name in NAMES that is defined as a macro, 581 call recognized_macro on it. */ 582 583static void 584check_macro_names (pfile, names) 585 cpp_reader *pfile; 586 namelist names; 587{ 588 size_t len; 589 while (*names) 590 { 591 len = strlen (names); 592 if (cpp_defined (pfile, (const unsigned char *)names, len)) 593 recognized_macro (names); 594 names += len + 1; 595 } 596} 597 598static void 599cb_file_change (pfile, map) 600 cpp_reader *pfile ATTRIBUTE_UNUSED; 601 const struct line_map *map; 602{ 603 /* Just keep track of current file name. */ 604 cur_file = map->to_file; 605} 606 607static void 608read_scan_file (in_fname, argc, argv) 609 char *in_fname; 610 int argc; 611 char **argv; 612{ 613 cpp_reader *scan_in; 614 cpp_callbacks *cb; 615 cpp_options *options; 616 struct fn_decl *fn; 617 int i; 618 struct symbol_list *cur_symbols; 619 620 obstack_init (&scan_file_obstack); 621 622 scan_in = cpp_create_reader (CLK_GNUC89); 623 cb = cpp_get_callbacks (scan_in); 624 cb->file_change = cb_file_change; 625 626 /* We are going to be scanning a header file out of its proper context, 627 so ignore warnings and errors. */ 628 options = cpp_get_options (scan_in); 629 options->inhibit_warnings = 1; 630 options->inhibit_errors = 1; 631 632 i = cpp_handle_options (scan_in, argc, argv); 633 if (i < argc) 634 cpp_error (scan_in, DL_ERROR, "invalid option `%s'", argv[i]); 635 if (cpp_errors (scan_in)) 636 exit (FATAL_EXIT_CODE); 637 638 if (! cpp_read_main_file (scan_in, in_fname, NULL)) 639 exit (FATAL_EXIT_CODE); 640 641 cpp_finish_options (scan_in); 642 643 /* We are scanning a system header, so mark it as such. */ 644 cpp_make_system_header (scan_in, 1, 0); 645 646 scan_decls (scan_in, argc, argv); 647 for (cur_symbols = &symbol_table[0]; cur_symbols->names; cur_symbols++) 648 check_macro_names (scan_in, cur_symbols->names); 649 650 /* Traditionally, getc and putc are defined in terms of _filbuf and _flsbuf. 651 If so, those functions are also required. */ 652 if (special_file_handling == stdio_h 653 && (fn = lookup_std_proto ("_filbuf", 7)) != NULL) 654 { 655 static const unsigned char getchar_call[] = "getchar();"; 656 int seen_filbuf = 0; 657 658 /* Scan the macro expansion of "getchar();". */ 659 cpp_push_buffer (scan_in, getchar_call, sizeof(getchar_call) - 1, 660 /* from_stage3 */ true, 1); 661 for (;;) 662 { 663 const cpp_token *t = cpp_get_token (scan_in); 664 665 if (t->type == CPP_EOF) 666 break; 667 else if (cpp_ideq (t, "_filbuf")) 668 seen_filbuf++; 669 } 670 671 if (seen_filbuf) 672 { 673 int need_filbuf = !SEEN (fn) && !REQUIRED (fn); 674 struct fn_decl *flsbuf_fn = lookup_std_proto ("_flsbuf", 7); 675 int need_flsbuf 676 = flsbuf_fn && !SEEN (flsbuf_fn) && !REQUIRED (flsbuf_fn); 677 678 /* Append "_filbuf" and/or "_flsbuf" to the required functions. */ 679 if (need_filbuf + need_flsbuf) 680 { 681 const char *new_list; 682 if (need_filbuf) 683 SET_REQUIRED (fn); 684 if (need_flsbuf) 685 SET_REQUIRED (flsbuf_fn); 686 if (need_flsbuf && need_filbuf) 687 new_list = "_filbuf\0_flsbuf\0"; 688 else if (need_flsbuf) 689 new_list = "_flsbuf\0"; 690 else /* if (need_flsbuf) */ 691 new_list = "_filbuf\0"; 692 add_symbols (ANSI_SYMBOL, new_list); 693 required_unseen_count += need_filbuf + need_flsbuf; 694 } 695 } 696 } 697 698 if (required_unseen_count + partial_count + required_other 699#if ADD_MISSING_EXTERN_C 700 + missing_extern_C_count 701#endif 702 == 0) 703 { 704 if (verbose) 705 fprintf (stderr, "%s: OK, nothing needs to be done.\n", inc_filename); 706 exit (SUCCESS_EXIT_CODE); 707 } 708 if (!verbose) 709 fprintf (stderr, "%s: fixing %s\n", progname, inc_filename); 710 else 711 { 712 if (required_unseen_count) 713 fprintf (stderr, "%s: %d missing function declarations.\n", 714 inc_filename, required_unseen_count); 715 if (partial_count) 716 fprintf (stderr, "%s: %d non-prototype function declarations.\n", 717 inc_filename, partial_count); 718#if ADD_MISSING_EXTERN_C 719 if (missing_extern_C_count) 720 fprintf (stderr, 721 "%s: %d declarations not protected by extern \"C\".\n", 722 inc_filename, missing_extern_C_count); 723#endif 724 } 725} 726 727static void 728write_rbrac () 729{ 730 struct fn_decl *fn; 731 const char *cptr; 732 struct symbol_list *cur_symbols; 733 734 if (required_unseen_count) 735 { 736#ifdef NO_IMPLICIT_EXTERN_C 737 fprintf (outf, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"); 738#endif 739 } 740 741 /* Now we print out prototypes for those functions that we haven't seen. */ 742 for (cur_symbols = &symbol_table[0]; cur_symbols->names; cur_symbols++) 743 { 744 int if_was_emitted = 0; 745 int name_len; 746 cptr = cur_symbols->names; 747 for ( ; (name_len = strlen (cptr)) != 0; cptr+= name_len + 1) 748 { 749 int macro_protect = 0; 750 751 if (cur_symbols->flags & MACRO_SYMBOL) 752 continue; 753 754 fn = lookup_std_proto (cptr, name_len); 755 if (fn == NULL || !REQUIRED (fn)) 756 continue; 757 758 if (!if_was_emitted) 759 { 760/* what about curses. ??? or _flsbuf/_filbuf ??? */ 761 if (cur_symbols->flags & ANSI_SYMBOL) 762 fprintf (outf, 763 "#if defined(__USE_FIXED_PROTOTYPES__) || defined(__cplusplus) || defined (__STRICT_ANSI__)\n"); 764 else if (cur_symbols->flags & (POSIX1_SYMBOL|POSIX2_SYMBOL)) 765 fprintf (outf, 766 "#if defined(__USE_FIXED_PROTOTYPES__) || (defined(__cplusplus) \\\n\ 767 ? (!defined(__STRICT_ANSI__) || defined(_POSIX_SOURCE)) \\\n\ 768 : (defined(__STRICT_ANSI__) && defined(_POSIX_SOURCE)))\n"); 769 else if (cur_symbols->flags & XOPEN_SYMBOL) 770 { 771 fprintf (outf, 772 "#if defined(__USE_FIXED_PROTOTYPES__) \\\n\ 773 || (defined(__STRICT_ANSI__) && defined(_XOPEN_SOURCE))\n"); 774 } 775 else if (cur_symbols->flags & XOPEN_EXTENDED_SYMBOL) 776 { 777 fprintf (outf, 778 "#if defined(__USE_FIXED_PROTOTYPES__) \\\n\ 779 || (defined(__STRICT_ANSI__) && defined(_XOPEN_EXTENDED_SOURCE))\n"); 780 } 781 else 782 { 783 fatal ("internal error for function %s", fn->fname); 784 } 785 if_was_emitted = 1; 786 } 787 788 /* In the case of memmove, protect in case the application 789 defines it as a macro before including the header. */ 790 if (!strcmp (fn->fname, "memmove") 791 || !strcmp (fn->fname, "putc") 792 || !strcmp (fn->fname, "getc") 793 || !strcmp (fn->fname, "vprintf") 794 || !strcmp (fn->fname, "vfprintf") 795 || !strcmp (fn->fname, "vsprintf") 796 || !strcmp (fn->fname, "rewinddir") 797 || !strcmp (fn->fname, "abort")) 798 macro_protect = 1; 799 800 if (macro_protect) 801 fprintf (outf, "#ifndef %s\n", fn->fname); 802 fprintf (outf, "extern %s %s (%s);\n", 803 fn->rtype, fn->fname, fn->params); 804 if (macro_protect) 805 fprintf (outf, "#endif\n"); 806 } 807 if (if_was_emitted) 808 fprintf (outf, 809 "#endif /* defined(__USE_FIXED_PROTOTYPES__) || ... */\n"); 810 } 811 if (required_unseen_count) 812 { 813#ifdef NO_IMPLICIT_EXTERN_C 814 fprintf (outf, "#ifdef __cplusplus\n}\n#endif\n"); 815#endif 816 } 817 818 switch (special_file_handling) 819 { 820 case errno_h: 821 if (!seen_errno) 822 fprintf (outf, "extern int errno;\n"); 823 break; 824 case stdlib_h: 825 if (!seen_EXIT_FAILURE) 826 fprintf (outf, "#define EXIT_FAILURE 1\n"); 827 if (!seen_EXIT_SUCCESS) 828 fprintf (outf, "#define EXIT_SUCCESS 0\n"); 829 break; 830 case sys_stat_h: 831 if (!seen_S_ISBLK && seen_S_IFBLK) 832 fprintf (outf, 833 "#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)\n"); 834 if (!seen_S_ISCHR && seen_S_IFCHR) 835 fprintf (outf, 836 "#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)\n"); 837 if (!seen_S_ISDIR && seen_S_IFDIR) 838 fprintf (outf, 839 "#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)\n"); 840 if (!seen_S_ISFIFO && seen_S_IFIFO) 841 fprintf (outf, 842 "#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)\n"); 843 if (!seen_S_ISLNK && seen_S_IFLNK) 844 fprintf (outf, 845 "#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)\n"); 846 if (!seen_S_ISREG && seen_S_IFREG) 847 fprintf (outf, 848 "#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)\n"); 849 break; 850 851 default: 852 break; 853 } 854 855 856#if ADD_MISSING_EXTERN_C 857 if (missing_extern_C_count + required_unseen_count > 0) 858 fprintf (outf, "#ifdef __cplusplus\n}\n#endif\n"); 859#endif 860} 861 862/* Returns 1 iff the file is properly protected from multiple inclusion: 863 #ifndef PROTECT_NAME 864 #define PROTECT_NAME 865 #endif 866 867 */ 868 869#define INF_GET() (inf_ptr < inf_limit ? *(unsigned char *) inf_ptr++ : EOF) 870#define INF_UNGET(c) ((c)!=EOF && inf_ptr--) 871 872static int 873inf_skip_spaces (c) 874 int c; 875{ 876 for (;;) 877 { 878 if (c == ' ' || c == '\t') 879 c = INF_GET (); 880 else if (c == '/') 881 { 882 c = INF_GET (); 883 if (c != '*') 884 { 885 (void) INF_UNGET (c); 886 return '/'; 887 } 888 c = INF_GET (); 889 for (;;) 890 { 891 if (c == EOF) 892 return EOF; 893 else if (c != '*') 894 { 895 if (c == '\n') 896 source_lineno++, lineno++; 897 c = INF_GET (); 898 } 899 else if ((c = INF_GET ()) == '/') 900 return INF_GET (); 901 } 902 } 903 else 904 break; 905 } 906 return c; 907} 908 909/* Read into STR from inf_buffer upto DELIM. */ 910 911static int 912inf_read_upto (str, delim) 913 sstring *str; 914 int delim; 915{ 916 int ch; 917 for (;;) 918 { 919 ch = INF_GET (); 920 if (ch == EOF || ch == delim) 921 break; 922 SSTRING_PUT (str, ch); 923 } 924 MAKE_SSTRING_SPACE (str, 1); 925 *str->ptr = 0; 926 return ch; 927} 928 929static int 930inf_scan_ident (s, c) 931 sstring *s; 932 int c; 933{ 934 s->ptr = s->base; 935 if (ISIDST (c)) 936 { 937 for (;;) 938 { 939 SSTRING_PUT (s, c); 940 c = INF_GET (); 941 if (c == EOF || !(ISIDNUM (c))) 942 break; 943 } 944 } 945 MAKE_SSTRING_SPACE (s, 1); 946 *s->ptr = 0; 947 return c; 948} 949 950/* Returns 1 if the file is correctly protected against multiple 951 inclusion, setting *ifndef_line to the line number of the initial #ifndef 952 and setting *endif_line to the final #endif. 953 Otherwise return 0. */ 954 955static int 956check_protection (ifndef_line, endif_line) 957 int *ifndef_line, *endif_line; 958{ 959 int c; 960 int if_nesting = 1; /* Level of nesting of #if's */ 961 char *protect_name = NULL; /* Identifier following initial #ifndef */ 962 int define_seen = 0; 963 964 /* Skip initial white space (including comments). */ 965 for (;; lineno++) 966 { 967 c = inf_skip_spaces (' '); 968 if (c == EOF) 969 return 0; 970 if (c != '\n') 971 break; 972 } 973 if (c != '#') 974 return 0; 975 c = inf_scan_ident (&buf, inf_skip_spaces (' ')); 976 if (SSTRING_LENGTH (&buf) == 0 || strcmp (buf.base, "ifndef") != 0) 977 return 0; 978 979 /* So far so good: We've seen an initial #ifndef. */ 980 *ifndef_line = lineno; 981 c = inf_scan_ident (&buf, inf_skip_spaces (c)); 982 if (SSTRING_LENGTH (&buf) == 0 || c == EOF) 983 return 0; 984 protect_name = xstrdup (buf.base); 985 986 (void) INF_UNGET (c); 987 c = inf_read_upto (&buf, '\n'); 988 if (c == EOF) 989 return 0; 990 lineno++; 991 992 for (;;) 993 { 994 c = inf_skip_spaces (' '); 995 if (c == EOF) 996 return 0; 997 if (c == '\n') 998 { 999 lineno++; 1000 continue; 1001 } 1002 if (c != '#') 1003 goto skip_to_eol; 1004 c = inf_scan_ident (&buf, inf_skip_spaces (' ')); 1005 if (SSTRING_LENGTH (&buf) == 0) 1006 ; 1007 else if (!strcmp (buf.base, "ifndef") 1008 || !strcmp (buf.base, "ifdef") || !strcmp (buf.base, "if")) 1009 { 1010 if_nesting++; 1011 } 1012 else if (!strcmp (buf.base, "endif")) 1013 { 1014 if_nesting--; 1015 if (if_nesting == 0) 1016 break; 1017 } 1018 else if (!strcmp (buf.base, "else")) 1019 { 1020 if (if_nesting == 1) 1021 return 0; 1022 } 1023 else if (!strcmp (buf.base, "define")) 1024 { 1025 c = inf_skip_spaces (c); 1026 c = inf_scan_ident (&buf, c); 1027 if (buf.base[0] > 0 && strcmp (buf.base, protect_name) == 0) 1028 define_seen = 1; 1029 } 1030 skip_to_eol: 1031 for (;;) 1032 { 1033 if (c == '\n' || c == EOF) 1034 break; 1035 c = INF_GET (); 1036 } 1037 if (c == EOF) 1038 return 0; 1039 lineno++; 1040 } 1041 1042 if (!define_seen) 1043 return 0; 1044 *endif_line = lineno; 1045 /* Skip final white space (including comments). */ 1046 for (;;) 1047 { 1048 c = inf_skip_spaces (' '); 1049 if (c == EOF) 1050 break; 1051 if (c != '\n') 1052 return 0; 1053 } 1054 1055 return 1; 1056} 1057 1058extern int main PARAMS ((int, char **)); 1059 1060int 1061main (argc, argv) 1062 int argc; 1063 char **argv; 1064{ 1065 int inf_fd; 1066 struct stat sbuf; 1067 int c; 1068#ifdef FIXPROTO_IGNORE_LIST 1069 int i; 1070#endif 1071 const char *cptr; 1072 int ifndef_line; 1073 int endif_line; 1074 long to_read; 1075 long int inf_size; 1076 struct symbol_list *cur_symbols; 1077 1078 if (argv[0] && argv[0][0]) 1079 { 1080 char *p; 1081 1082 progname = 0; 1083 for (p = argv[0]; *p; p++) 1084 if (*p == '/') 1085 progname = p; 1086 progname = progname ? progname+1 : argv[0]; 1087 } 1088 1089 if (argc < 4) 1090 { 1091 fprintf (stderr, "%s: Usage: foo.h infile.h outfile.h options\n", 1092 progname); 1093 exit (FATAL_EXIT_CODE); 1094 } 1095 1096 inc_filename = argv[1]; 1097 inc_filename_length = strlen (inc_filename); 1098 1099#ifdef FIXPROTO_IGNORE_LIST 1100 for (i = 0; files_to_ignore[i] != NULL; i++) 1101 { 1102 const char *const ignore_name = files_to_ignore[i]; 1103 int ignore_len = strlen (ignore_name); 1104 if (strncmp (inc_filename, ignore_name, ignore_len) == 0) 1105 { 1106 if (ignore_name[ignore_len-1] == '/' 1107 || inc_filename[ignore_len] == '\0') 1108 { 1109 if (verbose) 1110 fprintf (stderr, "%s: ignoring %s\n", progname, inc_filename); 1111 exit (SUCCESS_EXIT_CODE); 1112 } 1113 } 1114 1115 } 1116#endif 1117 1118 if (strcmp (inc_filename, "sys/stat.h") == 0) 1119 special_file_handling = sys_stat_h; 1120 else if (strcmp (inc_filename, "errno.h") == 0) 1121 special_file_handling = errno_h, required_other++; 1122 else if (strcmp (inc_filename, "stdlib.h") == 0) 1123 special_file_handling = stdlib_h, required_other+=2; 1124 else if (strcmp (inc_filename, "stdio.h") == 0) 1125 special_file_handling = stdio_h; 1126 include_entry = std_include_table; 1127 while (include_entry->name != NULL 1128 && ((strcmp (include_entry->name, CONTINUED) == 0) 1129 || strcmp (inc_filename, include_entry->name) != 0)) 1130 include_entry++; 1131 1132 if (include_entry->name != NULL) 1133 { 1134 const struct std_include_entry *entry; 1135 cur_symbol_table_size = 0; 1136 for (entry = include_entry; ;) 1137 { 1138 if (entry->flags) 1139 add_symbols (entry->flags, entry->names); 1140 entry++; 1141 if (!entry->name || strcmp (entry->name, CONTINUED) != 0) 1142 break; 1143 } 1144 } 1145 else 1146 symbol_table[0].names = NULL; 1147 1148 /* Count and mark the prototypes required for this include file. */ 1149 for (cur_symbols = &symbol_table[0]; cur_symbols->names; cur_symbols++) 1150 { 1151 int name_len; 1152 if (cur_symbols->flags & MACRO_SYMBOL) 1153 continue; 1154 cptr = cur_symbols->names; 1155 for ( ; (name_len = strlen (cptr)) != 0; cptr+= name_len + 1) 1156 { 1157 struct fn_decl *fn = lookup_std_proto (cptr, name_len); 1158 required_unseen_count++; 1159 if (fn == NULL) 1160 fprintf (stderr, "Internal error: No prototype for %s\n", cptr); 1161 else 1162 SET_REQUIRED (fn); 1163 } 1164 } 1165 1166 read_scan_file (argv[2], argc - 4, argv + 4); 1167 1168 inf_fd = open (argv[2], O_RDONLY, 0666); 1169 if (inf_fd < 0) 1170 { 1171 fprintf (stderr, "%s: Cannot open '%s' for reading -", 1172 progname, argv[2]); 1173 perror (NULL); 1174 exit (FATAL_EXIT_CODE); 1175 } 1176 if (fstat (inf_fd, &sbuf) < 0) 1177 { 1178 fprintf (stderr, "%s: Cannot get size of '%s' -", progname, argv[2]); 1179 perror (NULL); 1180 exit (FATAL_EXIT_CODE); 1181 } 1182 inf_size = sbuf.st_size; 1183 inf_buffer = (char *) xmalloc (inf_size + 2); 1184 inf_ptr = inf_buffer; 1185 1186 to_read = inf_size; 1187 while (to_read > 0) 1188 { 1189 long i = read (inf_fd, inf_buffer + inf_size - to_read, to_read); 1190 if (i < 0) 1191 { 1192 fprintf (stderr, "%s: Failed to read '%s' -", progname, argv[2]); 1193 perror (NULL); 1194 exit (FATAL_EXIT_CODE); 1195 } 1196 if (i == 0) 1197 { 1198 inf_size -= to_read; 1199 break; 1200 } 1201 to_read -= i; 1202 } 1203 1204 close (inf_fd); 1205 1206 /* Inf_size may have changed if read was short (as on VMS) */ 1207 inf_buffer[inf_size] = '\n'; 1208 inf_buffer[inf_size + 1] = '\0'; 1209 inf_limit = inf_buffer + inf_size; 1210 1211 /* If file doesn't end with '\n', add one. */ 1212 if (inf_limit > inf_buffer && inf_limit[-1] != '\n') 1213 inf_limit++; 1214 1215 unlink (argv[3]); 1216 outf = fopen (argv[3], "w"); 1217 if (outf == NULL) 1218 { 1219 fprintf (stderr, "%s: Cannot open '%s' for writing -", 1220 progname, argv[3]); 1221 perror (NULL); 1222 exit (FATAL_EXIT_CODE); 1223 } 1224 1225 lineno = 1; 1226 1227 if (check_protection (&ifndef_line, &endif_line)) 1228 { 1229 lbrac_line = ifndef_line+1; 1230 rbrac_line = endif_line; 1231 } 1232 else 1233 { 1234 lbrac_line = 1; 1235 rbrac_line = -1; 1236 } 1237 1238 /* Reset input file. */ 1239 inf_ptr = inf_buffer; 1240 lineno = 1; 1241 1242 for (;;) 1243 { 1244 if (lineno == lbrac_line) 1245 write_lbrac (); 1246 if (lineno == rbrac_line) 1247 write_rbrac (); 1248 for (;;) 1249 { 1250 struct fn_decl *fn; 1251 c = INF_GET (); 1252 if (c == EOF) 1253 break; 1254 if (ISIDST (c)) 1255 { 1256 c = inf_scan_ident (&buf, c); 1257 (void) INF_UNGET (c); 1258 fputs (buf.base, outf); 1259 fn = lookup_std_proto (buf.base, strlen (buf.base)); 1260 /* We only want to edit the declaration matching the one 1261 seen by scan-decls, as there can be multiple 1262 declarations, selected by #ifdef __STDC__ or whatever. */ 1263 if (fn && fn->partial && fn->partial->line_seen == lineno) 1264 { 1265 c = inf_skip_spaces (' '); 1266 if (c == EOF) 1267 break; 1268 if (c == '(') 1269 { 1270 c = inf_skip_spaces (' '); 1271 if (c == ')') 1272 { 1273 fprintf (outf, " _PARAMS((%s))", fn->params); 1274 } 1275 else 1276 { 1277 putc ('(', outf); 1278 (void) INF_UNGET (c); 1279 } 1280 } 1281 else 1282 fprintf (outf, " %c", c); 1283 } 1284 } 1285 else 1286 { 1287 putc (c, outf); 1288 if (c == '\n') 1289 break; 1290 } 1291 } 1292 if (c == EOF) 1293 break; 1294 lineno++; 1295 } 1296 if (rbrac_line < 0) 1297 write_rbrac (); 1298 1299 fclose (outf); 1300 1301 return 0; 1302} 1303 1304 1305static void 1306v_fatal (str, ap) 1307 const char * str; 1308 va_list ap; 1309{ 1310 fprintf (stderr, "%s: %s: ", progname, inc_filename); 1311 vfprintf (stderr, str, ap); 1312 fprintf (stderr, "\n"); 1313 1314 exit (FATAL_EXIT_CODE); 1315} 1316 1317static void 1318fatal VPARAMS ((const char *str, ...)) 1319{ 1320 VA_OPEN (ap, str); 1321 VA_FIXEDARG (ap, const char *, str); 1322 1323 v_fatal (str, ap); 1324 VA_CLOSE (ap); 1325} 1326