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