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