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