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