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