apinames.c (11798B)
1 /* 2 * This little program is used to parse the FreeType headers and 3 * find the declaration of all public APIs. This is easy, because 4 * they all look like the following: 5 * 6 * FT_EXPORT( return_type ) 7 * function_name( function arguments ); 8 * 9 * You must pass the list of header files as arguments. Wildcards are 10 * accepted if you are using GCC for compilation (and probably by 11 * other compilers too). 12 * 13 * Author: FreeType team, 2005-2023 14 * 15 * This code is explicitly placed into the public domain. 16 * 17 */ 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <stdarg.h> 22 #include <string.h> 23 #include <ctype.h> 24 25 #include "vms_shorten_symbol.c" 26 27 #define PROGRAM_NAME "apinames" 28 #define PROGRAM_VERSION "0.5" 29 30 #define LINEBUFF_SIZE 1024 31 32 33 typedef enum OutputFormat_ 34 { 35 OUTPUT_LIST = 0, /* output the list of names, one per line */ 36 OUTPUT_WINDOWS_DEF, /* output a Windows .DEF file for Visual C++ or Mingw */ 37 OUTPUT_BORLAND_DEF, /* output a Windows .DEF file for Borland C++ */ 38 OUTPUT_WATCOM_LBC, /* output a Watcom Linker Command File */ 39 OUTPUT_VMS_OPT, /* output an OpenVMS Linker Option File */ 40 OUTPUT_NETWARE_IMP, /* output a NetWare ImportFile */ 41 OUTPUT_GNU_VERMAP /* output a version map for GNU or Solaris linker */ 42 43 } OutputFormat; 44 45 #define SUFFIX_VMS_64ADDR "64__" 46 47 static void 48 panic( const char* fmt, 49 ... ) 50 { 51 va_list ap; 52 53 54 fprintf( stderr, "PANIC: " ); 55 56 va_start( ap, fmt ); 57 vfprintf( stderr, fmt, ap ); 58 va_end( ap ); 59 60 fprintf( stderr, "\n" ); 61 62 exit(2); 63 } 64 65 66 typedef struct NameRec_ 67 { 68 char* name; 69 unsigned int hash; 70 71 } NameRec, *Name; 72 73 74 static Name the_names; 75 static int num_names; 76 static int max_names; 77 78 79 static void 80 names_add( const char* name, 81 const char* end, 82 OutputFormat format ) 83 { 84 unsigned int h; 85 int nn, len, len_suffix; 86 Name nm; 87 88 89 if ( end <= name ) 90 return; 91 92 /* compute hash value */ 93 len = (int)( end - name ); 94 h = 0; 95 96 for ( nn = 0; nn < len; nn++ ) 97 h = h * 33 + name[nn]; 98 99 /* check for an pre-existing name */ 100 for ( nn = 0; nn < num_names; nn++ ) 101 { 102 nm = the_names + nn; 103 104 if ( (int)nm->hash == h && 105 memcmp( name, nm->name, len ) == 0 && 106 nm->name[len] == 0 ) 107 return; 108 } 109 110 /* add new name */ 111 if ( num_names >= max_names ) 112 { 113 max_names += ( max_names >> 1 ) + 4; 114 the_names = (NameRec*)realloc( the_names, 115 sizeof ( the_names[0] ) * max_names ); 116 if ( !the_names ) 117 panic( "not enough memory" ); 118 } 119 nm = &the_names[num_names++]; 120 121 switch ( format ) 122 { 123 case OUTPUT_VMS_OPT: 124 /* VMS mode would join the symbol name with a suffix */ 125 len_suffix = sizeof ( SUFFIX_VMS_64ADDR ); 126 break; 127 default: 128 len_suffix = 0; 129 } 130 131 nm->hash = h; 132 nm->name = (char*)malloc( len + len_suffix + 1 ); 133 if ( !nm->name ) 134 panic( "not enough memory" ); 135 136 memcpy( nm->name, name, len ); 137 nm->name[len] = 0; 138 } 139 140 141 static int 142 name_compare( const void* name1, 143 const void* name2 ) 144 { 145 Name n1 = (Name)name1; 146 Name n2 = (Name)name2; 147 148 return strcmp( n1->name, n2->name ); 149 } 150 151 152 static void 153 names_sort( void ) 154 { 155 qsort( the_names, (size_t)num_names, 156 sizeof ( the_names[0] ), name_compare ); 157 } 158 159 160 static void 161 names_dump( FILE* out, 162 OutputFormat format, 163 const char* dll_name ) 164 { 165 int nn; 166 167 168 switch ( format ) 169 { 170 case OUTPUT_WINDOWS_DEF: 171 if ( dll_name ) 172 fprintf( out, "LIBRARY %s\n", dll_name ); 173 174 fprintf( out, "DESCRIPTION FreeType 2 DLL\n" ); 175 fprintf( out, "EXPORTS\n" ); 176 177 for ( nn = 0; nn < num_names; nn++ ) 178 fprintf( out, " %s\n", the_names[nn].name ); 179 180 break; 181 182 case OUTPUT_BORLAND_DEF: 183 if ( dll_name ) 184 fprintf( out, "LIBRARY %s\n", dll_name ); 185 186 fprintf( out, "DESCRIPTION FreeType 2 DLL\n" ); 187 fprintf( out, "EXPORTS\n" ); 188 189 for ( nn = 0; nn < num_names; nn++ ) 190 fprintf( out, " _%s\n", the_names[nn].name ); 191 192 break; 193 194 case OUTPUT_WATCOM_LBC: 195 { 196 char temp[512]; 197 const char* dot; 198 199 200 if ( !dll_name ) 201 { 202 fprintf( stderr, 203 "you must provide a DLL name with the -d option!\n" ); 204 exit( 4 ); 205 } 206 207 /* we must omit the `.dll' suffix from the library name */ 208 dot = strchr( dll_name, '.' ); 209 if ( dot ) 210 { 211 int len = dot - dll_name; 212 213 214 if ( len > (int)( sizeof ( temp ) - 1 ) ) 215 len = sizeof ( temp ) - 1; 216 217 memcpy( temp, dll_name, len ); 218 temp[len] = 0; 219 220 dll_name = (const char*)temp; 221 } 222 223 for ( nn = 0; nn < num_names; nn++ ) 224 fprintf( out, "++_%s.%s.%s\n", 225 the_names[nn].name, dll_name, the_names[nn].name ); 226 } 227 228 break; 229 230 case OUTPUT_VMS_OPT: 231 fprintf( out, "case_sensitive=YES\n" ); 232 233 for ( nn = 0; nn < num_names; nn++ ) 234 { 235 char short_symbol[32]; 236 237 238 if ( vms_shorten_symbol( the_names[nn].name, short_symbol, 1 ) == -1 ) 239 panic( "could not shorten name '%s'", the_names[nn].name ); 240 fprintf( out, "symbol_vector = ( %s = PROCEDURE)\n", short_symbol ); 241 242 /* Also emit a 64-bit symbol, as created by the `vms_auto64` tool. */ 243 /* It has the string '64__' appended to its name. */ 244 strcat( the_names[nn].name , SUFFIX_VMS_64ADDR ); 245 if ( vms_shorten_symbol( the_names[nn].name, short_symbol, 1 ) == -1 ) 246 panic( "could not shorten name '%s'", the_names[nn].name ); 247 fprintf( out, "symbol_vector = ( %s = PROCEDURE)\n", short_symbol ); 248 } 249 250 break; 251 252 case OUTPUT_NETWARE_IMP: 253 if ( dll_name ) 254 fprintf( out, " (%s)\n", dll_name ); 255 256 for ( nn = 0; nn < num_names - 1; nn++ ) 257 fprintf( out, " %s,\n", the_names[nn].name ); 258 fprintf( out, " %s\n", the_names[num_names - 1].name ); 259 260 break; 261 262 case OUTPUT_GNU_VERMAP: 263 fprintf( out, "{\n\tglobal:\n" ); 264 265 for ( nn = 0; nn < num_names; nn++ ) 266 fprintf( out, "\t\t%s;\n", the_names[nn].name ); 267 268 fprintf( out, "\tlocal:\n\t\t*;\n};\n" ); 269 270 break; 271 272 default: /* LIST */ 273 for ( nn = 0; nn < num_names; nn++ ) 274 fprintf( out, "%s\n", the_names[nn].name ); 275 276 break; 277 } 278 } 279 280 281 /* states of the line parser */ 282 283 typedef enum State_ 284 { 285 STATE_START = 0, /* waiting for FT_EXPORT keyword and return type */ 286 STATE_TYPE /* type was read, waiting for function name */ 287 288 } State; 289 290 291 static int 292 read_header_file( FILE* file, 293 int verbose, 294 OutputFormat format ) 295 { 296 static char buff[LINEBUFF_SIZE + 1]; 297 State state = STATE_START; 298 299 300 while ( !feof( file ) ) 301 { 302 char* p; 303 304 305 if ( !fgets( buff, LINEBUFF_SIZE, file ) ) 306 break; 307 308 p = buff; 309 310 /* skip leading whitespace */ 311 while ( *p == ' ' || *p == '\t' ) 312 p++; 313 314 /* skip empty lines */ 315 if ( *p == '\n' || *p == '\r' ) 316 continue; 317 318 switch ( state ) 319 { 320 case STATE_START: 321 if ( memcmp( p, "FT_EXPORT(", 10 ) != 0 ) 322 break; 323 324 p += 10; 325 for (;;) 326 { 327 if ( *p == 0 || *p == '\n' || *p == '\r' ) 328 goto NextLine; 329 330 if ( *p == ')' ) 331 { 332 p++; 333 break; 334 } 335 336 p++; 337 } 338 339 state = STATE_TYPE; 340 341 /* 342 * Sometimes, the name is just after `FT_EXPORT(...)', so skip 343 * whitespace and fall-through if we find an alphanumeric character. 344 */ 345 while ( *p == ' ' || *p == '\t' ) 346 p++; 347 348 if ( !isalpha( *p ) ) 349 break; 350 351 /* fall-through */ 352 353 case STATE_TYPE: 354 { 355 char* name = p; 356 357 358 while ( isalnum( *p ) || *p == '_' ) 359 p++; 360 361 if ( p > name ) 362 { 363 if ( verbose ) 364 fprintf( stderr, ">>> %.*s\n", (int)( p - name ), name ); 365 366 names_add( name, p, format ); 367 } 368 369 state = STATE_START; 370 } 371 372 break; 373 374 default: 375 ; 376 } 377 378 NextLine: 379 ; 380 } /* end of while loop */ 381 382 return 0; 383 } 384 385 386 static void 387 usage( void ) 388 { 389 static const char* const format = 390 "%s %s: extract FreeType API names from header files\n" 391 "\n" 392 "This program extracts the list of public FreeType API functions.\n" 393 "It receives a list of header files as an argument and\n" 394 "generates a sorted list of unique identifiers in various formats.\n" 395 "\n" 396 "usage: %s [options] header1 [header2 ...]\n" 397 "\n" 398 "options: - parse the contents of stdin, ignore arguments\n" 399 " -v verbose mode, output sent to standard error\n" 400 " -oFILE write output to FILE instead of standard output\n" 401 " -dNAME indicate DLL file name, 'freetype.dll' by default\n" 402 " -w output .DEF file for Visual C++ and Mingw\n" 403 " -wB output .DEF file for Borland C++\n" 404 " -wW output Watcom Linker Response File\n" 405 " -wV output OpenVMS Linker Options File\n" 406 " -wN output NetWare Import File\n" 407 " -wL output version map for GNU or Solaris linker\n" 408 "\n"; 409 410 fprintf( stderr, 411 format, 412 PROGRAM_NAME, 413 PROGRAM_VERSION, 414 PROGRAM_NAME ); 415 416 exit( 1 ); 417 } 418 419 420 int 421 main( int argc, 422 const char* const* argv ) 423 { 424 int from_stdin = 0; 425 int verbose = 0; 426 OutputFormat format = OUTPUT_LIST; /* the default */ 427 FILE* out = stdout; 428 const char* library_name = NULL; 429 430 431 if ( argc < 2 ) 432 usage(); 433 434 /* `-' used as a single argument means read source file from stdin */ 435 while ( argc > 1 && argv[1][0] == '-' ) 436 { 437 const char* arg = argv[1]; 438 439 440 switch ( arg[1] ) 441 { 442 case 'v': 443 verbose = 1; 444 445 break; 446 447 case 'o': 448 if ( arg[2] == 0 ) 449 { 450 if ( argc < 2 ) 451 usage(); 452 453 arg = argv[2]; 454 argv++; 455 argc--; 456 } 457 else 458 arg += 2; 459 460 out = fopen( arg, "wt" ); 461 if ( !out ) 462 { 463 fprintf( stderr, "could not open '%s' for writing\n", arg ); 464 exit( 3 ); 465 } 466 467 break; 468 469 case 'd': 470 if ( arg[2] == 0 ) 471 { 472 if ( argc < 2 ) 473 usage(); 474 475 arg = argv[2]; 476 argv++; 477 argc--; 478 } 479 else 480 arg += 2; 481 482 library_name = arg; 483 484 break; 485 486 case 'w': 487 format = OUTPUT_WINDOWS_DEF; 488 489 switch ( arg[2] ) 490 { 491 case 'B': 492 format = OUTPUT_BORLAND_DEF; 493 break; 494 495 case 'W': 496 format = OUTPUT_WATCOM_LBC; 497 break; 498 499 case 'V': 500 format = OUTPUT_VMS_OPT; 501 break; 502 503 case 'N': 504 format = OUTPUT_NETWARE_IMP; 505 break; 506 507 case 'L': 508 format = OUTPUT_GNU_VERMAP; 509 break; 510 511 case 0: 512 break; 513 514 default: 515 usage(); 516 } 517 518 break; 519 520 case 0: 521 from_stdin = 1; 522 523 break; 524 525 default: 526 usage(); 527 } 528 529 argc--; 530 argv++; 531 532 } /* end of while loop */ 533 534 if ( from_stdin ) 535 read_header_file( stdin, verbose, format ); 536 else 537 { 538 for ( --argc, argv++; argc > 0; argc--, argv++ ) 539 { 540 FILE* file = fopen( argv[0], "rb" ); 541 542 543 if ( !file ) 544 fprintf( stderr, "unable to open '%s'\n", argv[0] ); 545 else 546 { 547 if ( verbose ) 548 fprintf( stderr, "opening '%s'\n", argv[0] ); 549 550 read_header_file( file, verbose, format ); 551 fclose( file ); 552 } 553 } 554 } 555 556 if ( num_names == 0 ) 557 panic( "could not find exported functions\n" ); 558 559 names_sort(); 560 names_dump( out, format, library_name ); 561 562 if ( out != stdout ) 563 fclose( out ); 564 565 return 0; 566 } 567 568 569 /* END */