Home | History | Annotate | Download | only in tools
      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.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
     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
     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
    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
    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
    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         char         temp[512];
    160 
    161 
    162         if ( dll_name == NULL )
    163         {
    164           fprintf( stderr,
    165                    "you must provide a DLL name with the -d option!\n" );
    166           exit( 4 );
    167         }
    168 
    169         /* we must omit the .dll suffix from the library name */
    170         dot = strchr( dll_name, '.' );
    171         if ( dot != NULL )
    172         {
    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
    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
    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 
    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