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