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
panic(const char * message)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
names_add(const char * name,const char * end)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
name_compare(const void * name1,const void * name2)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
names_sort(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
names_dump(FILE * out,OutputFormat format,const char * dll_name)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
read_header_file(FILE * file,int verbose)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
usage(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
main(int argc,const char * const * argv)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