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 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 #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* |
|
69 ft_bzip2_alloc( FT_Memory memory, |
|
70 int items, |
|
71 int size ) |
|
72 { |
|
73 FT_ULong sz = (FT_ULong)size * 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 |
|
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 |
|
120 ft_bzip2_check_header( FT_Stream stream ) |
|
121 { |
|
122 FT_Error error = Bzip2_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 = Bzip2_Err_Invalid_File_Format; |
|
137 goto Exit; |
|
138 } |
|
139 |
|
140 Exit: |
|
141 return error; |
|
142 } |
|
143 |
|
144 |
|
145 static FT_Error |
|
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 = Bzip2_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 = Bzip2_Err_Invalid_File_Format; |
|
185 |
|
186 Exit: |
|
187 return error; |
|
188 } |
|
189 |
|
190 |
|
191 static void |
|
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 |
|
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 |
|
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 return Bzip2_Err_Invalid_Stream_Operation; |
|
258 } |
|
259 else |
|
260 { |
|
261 size = stream->size - stream->pos; |
|
262 if ( size > FT_BZIP2_BUFFER_SIZE ) |
|
263 size = FT_BZIP2_BUFFER_SIZE; |
|
264 |
|
265 if ( size == 0 ) |
|
266 return Bzip2_Err_Invalid_Stream_Operation; |
|
267 |
|
268 FT_MEM_COPY( zip->input, stream->base + stream->pos, size ); |
|
269 } |
|
270 stream->pos += size; |
|
271 |
|
272 bzstream->next_in = (char*)zip->input; |
|
273 bzstream->avail_in = size; |
|
274 |
|
275 return Bzip2_Err_Ok; |
|
276 } |
|
277 |
|
278 |
|
279 static FT_Error |
|
280 ft_bzip2_file_fill_output( FT_BZip2File zip ) |
|
281 { |
|
282 bz_stream* bzstream = &zip->bzstream; |
|
283 FT_Error error = Bzip2_Err_Ok; |
|
284 |
|
285 |
|
286 zip->cursor = zip->buffer; |
|
287 bzstream->next_out = (char*)zip->cursor; |
|
288 bzstream->avail_out = FT_BZIP2_BUFFER_SIZE; |
|
289 |
|
290 while ( bzstream->avail_out > 0 ) |
|
291 { |
|
292 int err; |
|
293 |
|
294 |
|
295 if ( bzstream->avail_in == 0 ) |
|
296 { |
|
297 error = ft_bzip2_file_fill_input( zip ); |
|
298 if ( error ) |
|
299 break; |
|
300 } |
|
301 |
|
302 err = BZ2_bzDecompress( bzstream ); |
|
303 |
|
304 if ( err == BZ_STREAM_END ) |
|
305 { |
|
306 zip->limit = (FT_Byte*)bzstream->next_out; |
|
307 if ( zip->limit == zip->cursor ) |
|
308 error = Bzip2_Err_Invalid_Stream_Operation; |
|
309 break; |
|
310 } |
|
311 else if ( err != BZ_OK ) |
|
312 { |
|
313 error = Bzip2_Err_Invalid_Stream_Operation; |
|
314 break; |
|
315 } |
|
316 } |
|
317 |
|
318 return error; |
|
319 } |
|
320 |
|
321 |
|
322 /* fill output buffer; `count' must be <= FT_BZIP2_BUFFER_SIZE */ |
|
323 static FT_Error |
|
324 ft_bzip2_file_skip_output( FT_BZip2File zip, |
|
325 FT_ULong count ) |
|
326 { |
|
327 FT_Error error = Bzip2_Err_Ok; |
|
328 FT_ULong delta; |
|
329 |
|
330 |
|
331 for (;;) |
|
332 { |
|
333 delta = (FT_ULong)( zip->limit - zip->cursor ); |
|
334 if ( delta >= count ) |
|
335 delta = count; |
|
336 |
|
337 zip->cursor += delta; |
|
338 zip->pos += delta; |
|
339 |
|
340 count -= delta; |
|
341 if ( count == 0 ) |
|
342 break; |
|
343 |
|
344 error = ft_bzip2_file_fill_output( zip ); |
|
345 if ( error ) |
|
346 break; |
|
347 } |
|
348 |
|
349 return error; |
|
350 } |
|
351 |
|
352 |
|
353 static FT_ULong |
|
354 ft_bzip2_file_io( FT_BZip2File zip, |
|
355 FT_ULong pos, |
|
356 FT_Byte* buffer, |
|
357 FT_ULong count ) |
|
358 { |
|
359 FT_ULong result = 0; |
|
360 FT_Error error; |
|
361 |
|
362 |
|
363 /* Reset inflate stream if we're seeking backwards. */ |
|
364 /* Yes, that is not too efficient, but it saves memory :-) */ |
|
365 if ( pos < zip->pos ) |
|
366 { |
|
367 error = ft_bzip2_file_reset( zip ); |
|
368 if ( error ) |
|
369 goto Exit; |
|
370 } |
|
371 |
|
372 /* skip unwanted bytes */ |
|
373 if ( pos > zip->pos ) |
|
374 { |
|
375 error = ft_bzip2_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); |
|
376 if ( error ) |
|
377 goto Exit; |
|
378 } |
|
379 |
|
380 if ( count == 0 ) |
|
381 goto Exit; |
|
382 |
|
383 /* now read the data */ |
|
384 for (;;) |
|
385 { |
|
386 FT_ULong delta; |
|
387 |
|
388 |
|
389 delta = (FT_ULong)( zip->limit - zip->cursor ); |
|
390 if ( delta >= count ) |
|
391 delta = count; |
|
392 |
|
393 FT_MEM_COPY( buffer, zip->cursor, delta ); |
|
394 buffer += delta; |
|
395 result += delta; |
|
396 zip->cursor += delta; |
|
397 zip->pos += delta; |
|
398 |
|
399 count -= delta; |
|
400 if ( count == 0 ) |
|
401 break; |
|
402 |
|
403 error = ft_bzip2_file_fill_output( zip ); |
|
404 if ( error ) |
|
405 break; |
|
406 } |
|
407 |
|
408 Exit: |
|
409 return result; |
|
410 } |
|
411 |
|
412 |
|
413 /***************************************************************************/ |
|
414 /***************************************************************************/ |
|
415 /***** *****/ |
|
416 /***** B Z E M B E D D I N G S T R E A M *****/ |
|
417 /***** *****/ |
|
418 /***************************************************************************/ |
|
419 /***************************************************************************/ |
|
420 |
|
421 static void |
|
422 ft_bzip2_stream_close( FT_Stream stream ) |
|
423 { |
|
424 FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; |
|
425 FT_Memory memory = stream->memory; |
|
426 |
|
427 |
|
428 if ( zip ) |
|
429 { |
|
430 /* finalize bzip file descriptor */ |
|
431 ft_bzip2_file_done( zip ); |
|
432 |
|
433 FT_FREE( zip ); |
|
434 |
|
435 stream->descriptor.pointer = NULL; |
|
436 } |
|
437 } |
|
438 |
|
439 |
|
440 static FT_ULong |
|
441 ft_bzip2_stream_io( FT_Stream stream, |
|
442 FT_ULong pos, |
|
443 FT_Byte* buffer, |
|
444 FT_ULong count ) |
|
445 { |
|
446 FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; |
|
447 |
|
448 |
|
449 return ft_bzip2_file_io( zip, pos, buffer, count ); |
|
450 } |
|
451 |
|
452 |
|
453 FT_EXPORT_DEF( FT_Error ) |
|
454 FT_Stream_OpenBzip2( FT_Stream stream, |
|
455 FT_Stream source ) |
|
456 { |
|
457 FT_Error error; |
|
458 FT_Memory memory = source->memory; |
|
459 FT_BZip2File zip; |
|
460 |
|
461 |
|
462 /* |
|
463 * check the header right now; this prevents allocating unnecessary |
|
464 * objects when we don't need them |
|
465 */ |
|
466 error = ft_bzip2_check_header( source ); |
|
467 if ( error ) |
|
468 goto Exit; |
|
469 |
|
470 FT_ZERO( stream ); |
|
471 stream->memory = memory; |
|
472 |
|
473 if ( !FT_QNEW( zip ) ) |
|
474 { |
|
475 error = ft_bzip2_file_init( zip, stream, source ); |
|
476 if ( error ) |
|
477 { |
|
478 FT_FREE( zip ); |
|
479 goto Exit; |
|
480 } |
|
481 |
|
482 stream->descriptor.pointer = zip; |
|
483 } |
|
484 |
|
485 stream->size = 0x7FFFFFFFL; /* don't know the real size! */ |
|
486 stream->pos = 0; |
|
487 stream->base = 0; |
|
488 stream->read = ft_bzip2_stream_io; |
|
489 stream->close = ft_bzip2_stream_close; |
|
490 |
|
491 Exit: |
|
492 return error; |
|
493 } |
|
494 |
|
495 #else /* !FT_CONFIG_OPTION_USE_BZIP2 */ |
|
496 |
|
497 FT_EXPORT_DEF( FT_Error ) |
|
498 FT_Stream_OpenBzip2( FT_Stream stream, |
|
499 FT_Stream source ) |
|
500 { |
|
501 FT_UNUSED( stream ); |
|
502 FT_UNUSED( source ); |
|
503 |
|
504 return Bzip2_Err_Unimplemented_Feature; |
|
505 } |
|
506 |
|
507 #endif /* !FT_CONFIG_OPTION_USE_BZIP2 */ |
|
508 |
|
509 |
|
510 /* END */ |
|