Home | History | Annotate | Download | only in bzip2
      1 /****************************************************************************
      2  *
      3  * ftbzip2.c
      4  *
      5  *   FreeType support for .bz2 compressed files.
      6  *
      7  * This optional component relies on libbz2.  It should mainly be used to
      8  * parse compressed PCF fonts, as found with many X11 server
      9  * distributions.
     10  *
     11  * Copyright 2010-2018 by
     12  * Joel Klinghed.
     13  *
     14  * based on `src/gzip/ftgzip.c'
     15  *
     16  * This file is part of the FreeType project, and may only be used,
     17  * modified, and distributed under the terms of the FreeType project
     18  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
     19  * this file you indicate that you have read the license and
     20  * understand and accept it fully.
     21  *
     22  */
     23 
     24 
     25 #include <ft2build.h>
     26 #include FT_INTERNAL_MEMORY_H
     27 #include FT_INTERNAL_STREAM_H
     28 #include FT_INTERNAL_DEBUG_H
     29 #include FT_BZIP2_H
     30 #include FT_CONFIG_STANDARD_LIBRARY_H
     31 
     32 
     33 #include FT_MODULE_ERRORS_H
     34 
     35 #undef FTERRORS_H_
     36 
     37 #undef  FT_ERR_PREFIX
     38 #define FT_ERR_PREFIX  Bzip2_Err_
     39 #define FT_ERR_BASE    FT_Mod_Err_Bzip2
     40 
     41 #include FT_ERRORS_H
     42 
     43 
     44 #ifdef FT_CONFIG_OPTION_USE_BZIP2
     45 
     46 #define BZ_NO_STDIO /* Do not need FILE */
     47 #include <bzlib.h>
     48 
     49 
     50 /***************************************************************************/
     51 /***************************************************************************/
     52 /*****                                                                 *****/
     53 /*****           B Z I P 2   M E M O R Y   M A N A G E M E N T         *****/
     54 /*****                                                                 *****/
     55 /***************************************************************************/
     56 /***************************************************************************/
     57 
     58   /* it is better to use FreeType memory routines instead of raw
     59      'malloc/free' */
     60 
     61   typedef void *(* alloc_func)(void*, int, int);
     62   typedef void (* free_func)(void*, void*);
     63 
     64   static void*
     65   ft_bzip2_alloc( FT_Memory  memory,
     66                   int        items,
     67                   int        size )
     68   {
     69     FT_ULong    sz = (FT_ULong)size * (FT_ULong)items;
     70     FT_Error    error;
     71     FT_Pointer  p  = NULL;
     72 
     73 
     74     (void)FT_ALLOC( p, sz );
     75     return p;
     76   }
     77 
     78 
     79   static void
     80   ft_bzip2_free( FT_Memory  memory,
     81                  void*      address )
     82   {
     83     FT_MEM_FREE( address );
     84   }
     85 
     86 
     87 /***************************************************************************/
     88 /***************************************************************************/
     89 /*****                                                                 *****/
     90 /*****              B Z I P 2   F I L E   D E S C R I P T O R          *****/
     91 /*****                                                                 *****/
     92 /***************************************************************************/
     93 /***************************************************************************/
     94 
     95 #define FT_BZIP2_BUFFER_SIZE  4096
     96 
     97   typedef struct  FT_BZip2FileRec_
     98   {
     99     FT_Stream  source;         /* parent/source stream        */
    100     FT_Stream  stream;         /* embedding stream            */
    101     FT_Memory  memory;         /* memory allocator            */
    102     bz_stream  bzstream;       /* bzlib input stream          */
    103 
    104     FT_Byte    input[FT_BZIP2_BUFFER_SIZE];  /* input read buffer  */
    105 
    106     FT_Byte    buffer[FT_BZIP2_BUFFER_SIZE]; /* output buffer      */
    107     FT_ULong   pos;                          /* position in output */
    108     FT_Byte*   cursor;
    109     FT_Byte*   limit;
    110 
    111   } FT_BZip2FileRec, *FT_BZip2File;
    112 
    113 
    114   /* check and skip .bz2 header - we don't support `transparent' compression */
    115   static FT_Error
    116   ft_bzip2_check_header( FT_Stream  stream )
    117   {
    118     FT_Error  error = FT_Err_Ok;
    119     FT_Byte   head[4];
    120 
    121 
    122     if ( FT_STREAM_SEEK( 0 )       ||
    123          FT_STREAM_READ( head, 4 ) )
    124       goto Exit;
    125 
    126     /* head[0] && head[1] are the magic numbers;    */
    127     /* head[2] is the version, and head[3] the blocksize */
    128     if ( head[0] != 0x42  ||
    129          head[1] != 0x5A  ||
    130          head[2] != 0x68  )  /* only support bzip2 (huffman) */
    131     {
    132       error = FT_THROW( Invalid_File_Format );
    133       goto Exit;
    134     }
    135 
    136   Exit:
    137     return error;
    138   }
    139 
    140 
    141   static FT_Error
    142   ft_bzip2_file_init( FT_BZip2File  zip,
    143                       FT_Stream     stream,
    144                       FT_Stream     source )
    145   {
    146     bz_stream*  bzstream = &zip->bzstream;
    147     FT_Error    error    = FT_Err_Ok;
    148 
    149 
    150     zip->stream = stream;
    151     zip->source = source;
    152     zip->memory = stream->memory;
    153 
    154     zip->limit  = zip->buffer + FT_BZIP2_BUFFER_SIZE;
    155     zip->cursor = zip->limit;
    156     zip->pos    = 0;
    157 
    158     /* check .bz2 header */
    159     {
    160       stream = source;
    161 
    162       error = ft_bzip2_check_header( stream );
    163       if ( error )
    164         goto Exit;
    165 
    166       if ( FT_STREAM_SEEK( 0 ) )
    167         goto Exit;
    168     }
    169 
    170     /* initialize bzlib */
    171     bzstream->bzalloc = (alloc_func)ft_bzip2_alloc;
    172     bzstream->bzfree  = (free_func) ft_bzip2_free;
    173     bzstream->opaque  = zip->memory;
    174 
    175     bzstream->avail_in = 0;
    176     bzstream->next_in  = (char*)zip->buffer;
    177 
    178     if ( BZ2_bzDecompressInit( bzstream, 0, 0 ) != BZ_OK ||
    179          !bzstream->next_in                              )
    180       error = FT_THROW( Invalid_File_Format );
    181 
    182   Exit:
    183     return error;
    184   }
    185 
    186 
    187   static void
    188   ft_bzip2_file_done( FT_BZip2File  zip )
    189   {
    190     bz_stream*  bzstream = &zip->bzstream;
    191 
    192 
    193     BZ2_bzDecompressEnd( bzstream );
    194 
    195     /* clear the rest */
    196     bzstream->bzalloc   = NULL;
    197     bzstream->bzfree    = NULL;
    198     bzstream->opaque    = NULL;
    199     bzstream->next_in   = NULL;
    200     bzstream->next_out  = NULL;
    201     bzstream->avail_in  = 0;
    202     bzstream->avail_out = 0;
    203 
    204     zip->memory = NULL;
    205     zip->source = NULL;
    206     zip->stream = NULL;
    207   }
    208 
    209 
    210   static FT_Error
    211   ft_bzip2_file_reset( FT_BZip2File  zip )
    212   {
    213     FT_Stream  stream = zip->source;
    214     FT_Error   error;
    215 
    216 
    217     if ( !FT_STREAM_SEEK( 0 ) )
    218     {
    219       bz_stream*  bzstream = &zip->bzstream;
    220 
    221 
    222       BZ2_bzDecompressEnd( bzstream );
    223 
    224       bzstream->avail_in  = 0;
    225       bzstream->next_in   = (char*)zip->input;
    226       bzstream->avail_out = 0;
    227       bzstream->next_out  = (char*)zip->buffer;
    228 
    229       zip->limit  = zip->buffer + FT_BZIP2_BUFFER_SIZE;
    230       zip->cursor = zip->limit;
    231       zip->pos    = 0;
    232 
    233       BZ2_bzDecompressInit( bzstream, 0, 0 );
    234     }
    235 
    236     return error;
    237   }
    238 
    239 
    240   static FT_Error
    241   ft_bzip2_file_fill_input( FT_BZip2File  zip )
    242   {
    243     bz_stream*  bzstream = &zip->bzstream;
    244     FT_Stream   stream    = zip->source;
    245     FT_ULong    size;
    246 
    247 
    248     if ( stream->read )
    249     {
    250       size = stream->read( stream, stream->pos, zip->input,
    251                            FT_BZIP2_BUFFER_SIZE );
    252       if ( size == 0 )
    253       {
    254         zip->limit = zip->cursor;
    255         return FT_THROW( Invalid_Stream_Operation );
    256       }
    257     }
    258     else
    259     {
    260       size = stream->size - stream->pos;
    261       if ( size > FT_BZIP2_BUFFER_SIZE )
    262         size = FT_BZIP2_BUFFER_SIZE;
    263 
    264       if ( size == 0 )
    265       {
    266         zip->limit = zip->cursor;
    267         return FT_THROW( Invalid_Stream_Operation );
    268       }
    269 
    270       FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
    271     }
    272     stream->pos += size;
    273 
    274     bzstream->next_in  = (char*)zip->input;
    275     bzstream->avail_in = size;
    276 
    277     return FT_Err_Ok;
    278   }
    279 
    280 
    281   static FT_Error
    282   ft_bzip2_file_fill_output( FT_BZip2File  zip )
    283   {
    284     bz_stream*  bzstream = &zip->bzstream;
    285     FT_Error    error    = FT_Err_Ok;
    286 
    287 
    288     zip->cursor         = zip->buffer;
    289     bzstream->next_out  = (char*)zip->cursor;
    290     bzstream->avail_out = FT_BZIP2_BUFFER_SIZE;
    291 
    292     while ( bzstream->avail_out > 0 )
    293     {
    294       int  err;
    295 
    296 
    297       if ( bzstream->avail_in == 0 )
    298       {
    299         error = ft_bzip2_file_fill_input( zip );
    300         if ( error )
    301           break;
    302       }
    303 
    304       err = BZ2_bzDecompress( bzstream );
    305 
    306       if ( err == BZ_STREAM_END )
    307       {
    308         zip->limit = (FT_Byte*)bzstream->next_out;
    309         if ( zip->limit == zip->cursor )
    310           error = FT_THROW( Invalid_Stream_Operation );
    311         break;
    312       }
    313       else if ( err != BZ_OK )
    314       {
    315         zip->limit = zip->cursor;
    316         error      = FT_THROW( Invalid_Stream_Operation );
    317         break;
    318       }
    319     }
    320 
    321     return error;
    322   }
    323 
    324 
    325   /* fill output buffer; `count' must be <= FT_BZIP2_BUFFER_SIZE */
    326   static FT_Error
    327   ft_bzip2_file_skip_output( FT_BZip2File  zip,
    328                              FT_ULong      count )
    329   {
    330     FT_Error  error = FT_Err_Ok;
    331     FT_ULong  delta;
    332 
    333 
    334     for (;;)
    335     {
    336       delta = (FT_ULong)( zip->limit - zip->cursor );
    337       if ( delta >= count )
    338         delta = count;
    339 
    340       zip->cursor += delta;
    341       zip->pos    += delta;
    342 
    343       count -= delta;
    344       if ( count == 0 )
    345         break;
    346 
    347       error = ft_bzip2_file_fill_output( zip );
    348       if ( error )
    349         break;
    350     }
    351 
    352     return error;
    353   }
    354 
    355 
    356   static FT_ULong
    357   ft_bzip2_file_io( FT_BZip2File  zip,
    358                     FT_ULong      pos,
    359                     FT_Byte*      buffer,
    360                     FT_ULong      count )
    361   {
    362     FT_ULong  result = 0;
    363     FT_Error  error;
    364 
    365 
    366     /* Reset inflate stream if we're seeking backwards.        */
    367     /* Yes, that is not too efficient, but it saves memory :-) */
    368     if ( pos < zip->pos )
    369     {
    370       error = ft_bzip2_file_reset( zip );
    371       if ( error )
    372         goto Exit;
    373     }
    374 
    375     /* skip unwanted bytes */
    376     if ( pos > zip->pos )
    377     {
    378       error = ft_bzip2_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
    379       if ( error )
    380         goto Exit;
    381     }
    382 
    383     if ( count == 0 )
    384       goto Exit;
    385 
    386     /* now read the data */
    387     for (;;)
    388     {
    389       FT_ULong  delta;
    390 
    391 
    392       delta = (FT_ULong)( zip->limit - zip->cursor );
    393       if ( delta >= count )
    394         delta = count;
    395 
    396       FT_MEM_COPY( buffer, zip->cursor, delta );
    397       buffer      += delta;
    398       result      += delta;
    399       zip->cursor += delta;
    400       zip->pos    += delta;
    401 
    402       count -= delta;
    403       if ( count == 0 )
    404         break;
    405 
    406       error = ft_bzip2_file_fill_output( zip );
    407       if ( error )
    408         break;
    409     }
    410 
    411   Exit:
    412     return result;
    413   }
    414 
    415 
    416 /***************************************************************************/
    417 /***************************************************************************/
    418 /*****                                                                 *****/
    419 /*****               B Z   E M B E D D I N G   S T R E A M             *****/
    420 /*****                                                                 *****/
    421 /***************************************************************************/
    422 /***************************************************************************/
    423 
    424   static void
    425   ft_bzip2_stream_close( FT_Stream  stream )
    426   {
    427     FT_BZip2File  zip    = (FT_BZip2File)stream->descriptor.pointer;
    428     FT_Memory     memory = stream->memory;
    429 
    430 
    431     if ( zip )
    432     {
    433       /* finalize bzip file descriptor */
    434       ft_bzip2_file_done( zip );
    435 
    436       FT_FREE( zip );
    437 
    438       stream->descriptor.pointer = NULL;
    439     }
    440   }
    441 
    442 
    443   static unsigned long
    444   ft_bzip2_stream_io( FT_Stream       stream,
    445                       unsigned long   offset,
    446                       unsigned char*  buffer,
    447                       unsigned long   count )
    448   {
    449     FT_BZip2File  zip = (FT_BZip2File)stream->descriptor.pointer;
    450 
    451 
    452     return ft_bzip2_file_io( zip, offset, buffer, count );
    453   }
    454 
    455 
    456   FT_EXPORT_DEF( FT_Error )
    457   FT_Stream_OpenBzip2( FT_Stream  stream,
    458                        FT_Stream  source )
    459   {
    460     FT_Error      error;
    461     FT_Memory     memory;
    462     FT_BZip2File  zip = NULL;
    463 
    464 
    465     if ( !stream || !source )
    466     {
    467       error = FT_THROW( Invalid_Stream_Handle );
    468       goto Exit;
    469     }
    470 
    471     memory = source->memory;
    472 
    473     /*
    474      * check the header right now; this prevents allocating unnecessary
    475      * objects when we don't need them
    476      */
    477     error = ft_bzip2_check_header( source );
    478     if ( error )
    479       goto Exit;
    480 
    481     FT_ZERO( stream );
    482     stream->memory = memory;
    483 
    484     if ( !FT_QNEW( zip ) )
    485     {
    486       error = ft_bzip2_file_init( zip, stream, source );
    487       if ( error )
    488       {
    489         FT_FREE( zip );
    490         goto Exit;
    491       }
    492 
    493       stream->descriptor.pointer = zip;
    494     }
    495 
    496     stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
    497     stream->pos   = 0;
    498     stream->base  = 0;
    499     stream->read  = ft_bzip2_stream_io;
    500     stream->close = ft_bzip2_stream_close;
    501 
    502   Exit:
    503     return error;
    504   }
    505 
    506 #else  /* !FT_CONFIG_OPTION_USE_BZIP2 */
    507 
    508   FT_EXPORT_DEF( FT_Error )
    509   FT_Stream_OpenBzip2( FT_Stream  stream,
    510                        FT_Stream  source )
    511   {
    512     FT_UNUSED( stream );
    513     FT_UNUSED( source );
    514 
    515     return FT_THROW( Unimplemented_Feature );
    516   }
    517 
    518 #endif /* !FT_CONFIG_OPTION_USE_BZIP2 */
    519 
    520 
    521 /* END */
    522