• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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