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
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
panic(const char * message)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
names_add(const char * name,const char * end)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
name_compare(const void * name1,const void * name2)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
names_sort(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
names_dump(FILE * out,OutputFormat format,const char * dll_name)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
160
161 if ( dll_name == NULL )
162 {
163 fprintf( stderr,
164 "you must provide a DLL name with the -d option!\n" );
165 exit( 4 );
166 }
167
168 /* we must omit the .dll suffix from the library name */
169 dot = strchr( dll_name, '.' );
170 if ( dot != NULL )
171 {
172 char temp[512];
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
read_header_file(FILE * file,int verbose)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
usage(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
main(int argc,const char * const * argv)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