• 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 <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