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