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