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