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