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