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