2 * Copyright (c) 1995-1997 Sam Leffler
3 * Copyright (c) 1995-1997 Silicon Graphics, Inc.
5 * Permission to use, copy, modify, distribute, and sell this software and
6 * its documentation for any purpose is hereby granted without fee, provided
7 * that (i) the above copyright notices and this permission notice appear in
8 * all copies of the software and related documentation, and (ii) the names of
9 * Sam Leffler and Silicon Graphics may not be used in any advertising or
10 * publicity relating to the software without the specific, prior written
11 * permission of Sam Leffler and Silicon Graphics.
13 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
17 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
30 * ZIP (aka Deflate) Compression Support
32 * This file is an interface to the zlib library written by
33 * Jean-loup Gailly and Mark Adler. You must use version 1.0 or later
36 * Optionally, libdeflate (https://github.com/ebiggers/libdeflate) may be used
37 * to do the compression and decompression, but only for whole strips and tiles.
38 * For scanline access, zlib will be sued as a fallback.
40 #include "tif_predict.h"
43 #if LIBDEFLATE_SUPPORT
44 #include "libdeflate.h"
46 #define LIBDEFLATE_MAX_COMPRESSION_LEVEL 12
51 * Sigh, ZLIB_VERSION is defined as a string so there's no
52 * way to do a proper check here. Instead we guess based
53 * on the presence of #defines that were added between the
54 * 0.95 and 1.0 distributions.
56 #if !defined(Z_NO_COMPRESSION) || !defined(Z_DEFLATED)
57 #error "Antiquated ZLIB software; you must use version 1.0 or later"
60 #define SAFE_MSG(sp) ((sp)->stream.msg == NULL ? "" : (sp)->stream.msg)
63 * State block for each open TIFF
64 * file using ZIP compression/decompression.
67 TIFFPredictorState predict
;
69 int zipquality
; /* compression level */
70 int state
; /* state flags */
71 int subcodec
; /* DEFLATE_SUBCODEC_ZLIB or DEFLATE_SUBCODEC_LIBDEFLATE */
72 #if LIBDEFLATE_SUPPORT
73 int libdeflate_state
; /* -1 = until first time ZIPEncode() / ZIPDecode() is called, 0 = use zlib, 1 = use libdeflate */
74 struct libdeflate_decompressor
* libdeflate_dec
;
75 struct libdeflate_compressor
* libdeflate_enc
;
77 #define ZSTATE_INIT_DECODE 0x01
78 #define ZSTATE_INIT_ENCODE 0x02
80 TIFFVGetMethod vgetparent
; /* super-class method */
81 TIFFVSetMethod vsetparent
; /* super-class method */
84 #define ZState(tif) ((ZIPState*) (tif)->tif_data)
85 #define DecoderState(tif) ZState(tif)
86 #define EncoderState(tif) ZState(tif)
88 static int ZIPEncode(TIFF
* tif
, uint8_t* bp
, tmsize_t cc
, uint16_t s
);
89 static int ZIPDecode(TIFF
* tif
, uint8_t* op
, tmsize_t occ
, uint16_t s
);
92 ZIPFixupTags(TIFF
* tif
)
99 ZIPSetupDecode(TIFF
* tif
)
101 static const char module
[] = "ZIPSetupDecode";
102 ZIPState
* sp
= DecoderState(tif
);
106 /* if we were last encoding, terminate this mode */
107 if (sp
->state
& ZSTATE_INIT_ENCODE
) {
108 deflateEnd(&sp
->stream
);
112 /* This function can possibly be called several times by */
113 /* PredictorSetupDecode() if this function succeeds but */
114 /* PredictorSetup() fails */
115 if ((sp
->state
& ZSTATE_INIT_DECODE
) == 0 &&
116 inflateInit(&sp
->stream
) != Z_OK
) {
117 TIFFErrorExt(tif
->tif_clientdata
, module
, "%s", SAFE_MSG(sp
));
120 sp
->state
|= ZSTATE_INIT_DECODE
;
126 * Setup state for decoding a strip.
129 ZIPPreDecode(TIFF
* tif
, uint16_t s
)
131 ZIPState
* sp
= DecoderState(tif
);
136 if( (sp
->state
& ZSTATE_INIT_DECODE
) == 0 )
137 tif
->tif_setupdecode( tif
);
139 #if LIBDEFLATE_SUPPORT
140 sp
->libdeflate_state
= -1;
142 sp
->stream
.next_in
= tif
->tif_rawdata
;
143 assert(sizeof(sp
->stream
.avail_in
)==4); /* if this assert gets raised,
144 we need to simplify this code to reflect a ZLib that is likely updated
145 to deal with 8byte memory sizes, though this code will respond
146 appropriately even before we simplify it */
147 sp
->stream
.avail_in
= (uint64_t)tif
->tif_rawcc
< 0xFFFFFFFFU
? (uInt
) tif
->tif_rawcc
: 0xFFFFFFFFU
;
148 return (inflateReset(&sp
->stream
) == Z_OK
);
152 ZIPDecode(TIFF
* tif
, uint8_t* op
, tmsize_t occ
, uint16_t s
)
154 static const char module
[] = "ZIPDecode";
155 ZIPState
* sp
= DecoderState(tif
);
159 assert(sp
->state
== ZSTATE_INIT_DECODE
);
161 #if LIBDEFLATE_SUPPORT
162 if( sp
->libdeflate_state
== 1 )
165 /* If we have libdeflate support and we are asked to read a whole */
166 /* strip/tile, then go for using it */
168 TIFFDirectory
*td
= &tif
->tif_dir
;
170 if( sp
->libdeflate_state
== 0 )
172 if( sp
->subcodec
== DEFLATE_SUBCODEC_ZLIB
)
175 /* Check if we are in the situation where we can use libdeflate */
177 if( TIFFTileSize64(tif
) != (uint64_t)occ
)
180 uint32_t strip_height
= td
->td_imagelength
- tif
->tif_row
;
181 if (strip_height
> td
->td_rowsperstrip
)
182 strip_height
= td
->td_rowsperstrip
;
183 if( TIFFVStripSize64(tif
, strip_height
) != (uint64_t)occ
)
187 /* Check for overflow */
188 if( (size_t)tif
->tif_rawcc
!= (uint64_t)tif
->tif_rawcc
)
190 if( (size_t)occ
!= (uint64_t)occ
)
193 /* Go for decompression using libdeflate */
195 enum libdeflate_result res
;
196 if( sp
->libdeflate_dec
== NULL
)
198 sp
->libdeflate_dec
= libdeflate_alloc_decompressor();
199 if( sp
->libdeflate_dec
== NULL
)
205 sp
->libdeflate_state
= 1;
207 res
= libdeflate_zlib_decompress(
208 sp
->libdeflate_dec
, tif
->tif_rawcp
, (size_t)tif
->tif_rawcc
, op
, (size_t)occ
, NULL
);
210 tif
->tif_rawcp
+= tif
->tif_rawcc
;
213 /* We accept LIBDEFLATE_INSUFFICIENT_SPACE has a return */
214 /* There are odd files in the wild where the last strip, when */
215 /* it is smaller in height than td_rowsperstrip, actually contains */
216 /* data for td_rowsperstrip lines. Just ignore that silently. */
217 if( res
!= LIBDEFLATE_SUCCESS
&&
218 res
!= LIBDEFLATE_INSUFFICIENT_SPACE
)
220 TIFFErrorExt(tif
->tif_clientdata
, module
,
221 "Decoding error at scanline %lu",
222 (unsigned long) tif
->tif_row
);
229 sp
->libdeflate_state
= 0;
230 #endif /* LIBDEFLATE_SUPPORT */
232 sp
->stream
.next_in
= tif
->tif_rawcp
;
234 sp
->stream
.next_out
= op
;
235 assert(sizeof(sp
->stream
.avail_out
)==4); /* if this assert gets raised,
236 we need to simplify this code to reflect a ZLib that is likely updated
237 to deal with 8byte memory sizes, though this code will respond
238 appropriately even before we simplify it */
241 uInt avail_in_before
= (uint64_t)tif
->tif_rawcc
<= 0xFFFFFFFFU
? (uInt
)tif
->tif_rawcc
: 0xFFFFFFFFU
;
242 uInt avail_out_before
= (uint64_t)occ
< 0xFFFFFFFFU
? (uInt
) occ
: 0xFFFFFFFFU
;
243 sp
->stream
.avail_in
= avail_in_before
;
244 sp
->stream
.avail_out
= avail_out_before
;
245 state
= inflate(&sp
->stream
, Z_PARTIAL_FLUSH
);
246 tif
->tif_rawcc
-= (avail_in_before
- sp
->stream
.avail_in
);
247 occ
-= (avail_out_before
- sp
->stream
.avail_out
);
248 if (state
== Z_STREAM_END
)
250 if (state
== Z_DATA_ERROR
) {
251 TIFFErrorExt(tif
->tif_clientdata
, module
,
252 "Decoding error at scanline %lu, %s",
253 (unsigned long) tif
->tif_row
, SAFE_MSG(sp
));
257 TIFFErrorExt(tif
->tif_clientdata
, module
,
258 "ZLib error: %s", SAFE_MSG(sp
));
263 TIFFErrorExt(tif
->tif_clientdata
, module
,
264 "Not enough data at scanline %lu (short %" PRIu64
" bytes)",
265 (unsigned long) tif
->tif_row
, (uint64_t) occ
);
269 tif
->tif_rawcp
= sp
->stream
.next_in
;
275 ZIPSetupEncode(TIFF
* tif
)
277 static const char module
[] = "ZIPSetupEncode";
278 ZIPState
* sp
= EncoderState(tif
);
282 if (sp
->state
& ZSTATE_INIT_DECODE
) {
283 inflateEnd(&sp
->stream
);
287 cappedQuality
= sp
->zipquality
;
288 if( cappedQuality
> Z_BEST_COMPRESSION
)
289 cappedQuality
= Z_BEST_COMPRESSION
;
291 if (deflateInit(&sp
->stream
, cappedQuality
) != Z_OK
) {
292 TIFFErrorExt(tif
->tif_clientdata
, module
, "%s", SAFE_MSG(sp
));
295 sp
->state
|= ZSTATE_INIT_ENCODE
;
301 * Reset encoding state at the start of a strip.
304 ZIPPreEncode(TIFF
* tif
, uint16_t s
)
306 ZIPState
*sp
= EncoderState(tif
);
310 if( sp
->state
!= ZSTATE_INIT_ENCODE
)
311 tif
->tif_setupencode( tif
);
313 #if LIBDEFLATE_SUPPORT
314 sp
->libdeflate_state
= -1;
316 sp
->stream
.next_out
= tif
->tif_rawdata
;
317 assert(sizeof(sp
->stream
.avail_out
)==4); /* if this assert gets raised,
318 we need to simplify this code to reflect a ZLib that is likely updated
319 to deal with 8byte memory sizes, though this code will respond
320 appropriately even before we simplify it */
321 sp
->stream
.avail_out
= (uint64_t)tif
->tif_rawdatasize
<= 0xFFFFFFFFU
? (uInt
)tif
->tif_rawdatasize
: 0xFFFFFFFFU
;
322 return (deflateReset(&sp
->stream
) == Z_OK
);
326 * Encode a chunk of pixels.
329 ZIPEncode(TIFF
* tif
, uint8_t* bp
, tmsize_t cc
, uint16_t s
)
331 static const char module
[] = "ZIPEncode";
332 ZIPState
*sp
= EncoderState(tif
);
335 assert(sp
->state
== ZSTATE_INIT_ENCODE
);
339 #if LIBDEFLATE_SUPPORT
340 if( sp
->libdeflate_state
== 1 )
343 /* If we have libdeflate support and we are asked to write a whole */
344 /* strip/tile, then go for using it */
346 TIFFDirectory
*td
= &tif
->tif_dir
;
348 if( sp
->libdeflate_state
== 0 )
350 if( sp
->subcodec
== DEFLATE_SUBCODEC_ZLIB
)
353 /* Libdeflate does not support the 0-compression level */
354 if( sp
->zipquality
== Z_NO_COMPRESSION
)
357 /* Check if we are in the situation where we can use libdeflate */
359 if( TIFFTileSize64(tif
) != (uint64_t)cc
)
362 uint32_t strip_height
= td
->td_imagelength
- tif
->tif_row
;
363 if (strip_height
> td
->td_rowsperstrip
)
364 strip_height
= td
->td_rowsperstrip
;
365 if( TIFFVStripSize64(tif
, strip_height
) != (uint64_t)cc
)
369 /* Check for overflow */
370 if( (size_t)tif
->tif_rawdatasize
!= (uint64_t)tif
->tif_rawdatasize
)
372 if( (size_t)cc
!= (uint64_t)cc
)
375 /* Go for compression using libdeflate */
377 size_t nCompressedBytes
;
378 if( sp
->libdeflate_enc
== NULL
)
380 /* To get results as good as zlib, we asked for an extra */
381 /* level of compression */
382 sp
->libdeflate_enc
= libdeflate_alloc_compressor(
383 sp
->zipquality
== Z_DEFAULT_COMPRESSION
? 7 :
384 sp
->zipquality
>= 6 && sp
->zipquality
<= 9 ? sp
->zipquality
+ 1 :
386 if( sp
->libdeflate_enc
== NULL
)
388 TIFFErrorExt(tif
->tif_clientdata
, module
,
389 "Cannot allocate compressor");
394 /* Make sure the output buffer is large enough for the worse case. */
395 /* In TIFFWriteBufferSetup(), when libtiff allocates the buffer */
396 /* we've taken a 10% margin over the uncompressed size, which should */
397 /* be large enough even for the the worse case scenario. */
398 if( libdeflate_zlib_compress_bound(sp
->libdeflate_enc
, (size_t)cc
) >
399 (size_t)tif
->tif_rawdatasize
)
404 sp
->libdeflate_state
= 1;
405 nCompressedBytes
= libdeflate_zlib_compress(
406 sp
->libdeflate_enc
, bp
, (size_t)cc
, tif
->tif_rawdata
, (size_t)tif
->tif_rawdatasize
);
408 if( nCompressedBytes
== 0 )
410 TIFFErrorExt(tif
->tif_clientdata
, module
,
411 "Encoder error at scanline %lu",
412 (unsigned long) tif
->tif_row
);
416 tif
->tif_rawcc
= nCompressedBytes
;
418 if( !TIFFFlushData1(tif
) )
424 sp
->libdeflate_state
= 0;
425 #endif /* LIBDEFLATE_SUPPORT */
427 sp
->stream
.next_in
= bp
;
428 assert(sizeof(sp
->stream
.avail_in
)==4); /* if this assert gets raised,
429 we need to simplify this code to reflect a ZLib that is likely updated
430 to deal with 8byte memory sizes, though this code will respond
431 appropriately even before we simplify it */
433 uInt avail_in_before
= (uint64_t)cc
<= 0xFFFFFFFFU
? (uInt
)cc
: 0xFFFFFFFFU
;
434 sp
->stream
.avail_in
= avail_in_before
;
435 if (deflate(&sp
->stream
, Z_NO_FLUSH
) != Z_OK
) {
436 TIFFErrorExt(tif
->tif_clientdata
, module
,
441 if (sp
->stream
.avail_out
== 0) {
442 tif
->tif_rawcc
= tif
->tif_rawdatasize
;
443 if (!TIFFFlushData1(tif
))
445 sp
->stream
.next_out
= tif
->tif_rawdata
;
446 sp
->stream
.avail_out
= (uint64_t)tif
->tif_rawdatasize
<= 0xFFFFFFFFU
? (uInt
)tif
->tif_rawdatasize
: 0xFFFFFFFFU
;
448 cc
-= (avail_in_before
- sp
->stream
.avail_in
);
454 * Finish off an encoded strip by flushing the last
455 * string and tacking on an End Of Information code.
458 ZIPPostEncode(TIFF
* tif
)
460 static const char module
[] = "ZIPPostEncode";
461 ZIPState
*sp
= EncoderState(tif
);
464 #if LIBDEFLATE_SUPPORT
465 if( sp
->libdeflate_state
== 1 )
469 sp
->stream
.avail_in
= 0;
471 state
= deflate(&sp
->stream
, Z_FINISH
);
475 if ((tmsize_t
)sp
->stream
.avail_out
!= tif
->tif_rawdatasize
)
477 tif
->tif_rawcc
= tif
->tif_rawdatasize
- sp
->stream
.avail_out
;
478 if (!TIFFFlushData1(tif
))
480 sp
->stream
.next_out
= tif
->tif_rawdata
;
481 sp
->stream
.avail_out
= (uint64_t)tif
->tif_rawdatasize
<= 0xFFFFFFFFU
? (uInt
)tif
->tif_rawdatasize
: 0xFFFFFFFFU
;
485 TIFFErrorExt(tif
->tif_clientdata
, module
,
486 "ZLib error: %s", SAFE_MSG(sp
));
489 } while (state
!= Z_STREAM_END
);
494 ZIPCleanup(TIFF
* tif
)
496 ZIPState
* sp
= ZState(tif
);
500 (void)TIFFPredictorCleanup(tif
);
502 tif
->tif_tagmethods
.vgetfield
= sp
->vgetparent
;
503 tif
->tif_tagmethods
.vsetfield
= sp
->vsetparent
;
505 if (sp
->state
& ZSTATE_INIT_ENCODE
) {
506 deflateEnd(&sp
->stream
);
508 } else if( sp
->state
& ZSTATE_INIT_DECODE
) {
509 inflateEnd(&sp
->stream
);
513 #if LIBDEFLATE_SUPPORT
514 if( sp
->libdeflate_dec
)
515 libdeflate_free_decompressor(sp
->libdeflate_dec
);
516 if( sp
->libdeflate_enc
)
517 libdeflate_free_compressor(sp
->libdeflate_enc
);
521 tif
->tif_data
= NULL
;
523 _TIFFSetDefaultCompressionState(tif
);
527 ZIPVSetField(TIFF
* tif
, uint32_t tag
, va_list ap
)
529 static const char module
[] = "ZIPVSetField";
530 ZIPState
* sp
= ZState(tif
);
533 case TIFFTAG_ZIPQUALITY
:
534 sp
->zipquality
= (int) va_arg(ap
, int);
535 if( sp
->zipquality
< Z_DEFAULT_COMPRESSION
||
536 sp
->zipquality
> LIBDEFLATE_MAX_COMPRESSION_LEVEL
) {
537 TIFFErrorExt(tif
->tif_clientdata
, module
,
538 "Invalid ZipQuality value. Should be in [-1,%d] range",
539 LIBDEFLATE_MAX_COMPRESSION_LEVEL
);
543 if ( sp
->state
&ZSTATE_INIT_ENCODE
) {
544 int cappedQuality
= sp
->zipquality
;
545 if( cappedQuality
> Z_BEST_COMPRESSION
)
546 cappedQuality
= Z_BEST_COMPRESSION
;
547 if (deflateParams(&sp
->stream
,
548 cappedQuality
, Z_DEFAULT_STRATEGY
) != Z_OK
) {
549 TIFFErrorExt(tif
->tif_clientdata
, module
, "ZLib error: %s",
555 #if LIBDEFLATE_SUPPORT
556 if( sp
->libdeflate_enc
)
558 libdeflate_free_compressor(sp
->libdeflate_enc
);
559 sp
->libdeflate_enc
= NULL
;
565 case TIFFTAG_DEFLATE_SUBCODEC
:
566 sp
->subcodec
= (int) va_arg(ap
, int);
567 if( sp
->subcodec
!= DEFLATE_SUBCODEC_ZLIB
&&
568 sp
->subcodec
!= DEFLATE_SUBCODEC_LIBDEFLATE
)
570 TIFFErrorExt(tif
->tif_clientdata
, module
,
571 "Invalid DeflateCodec value.");
574 #if !LIBDEFLATE_SUPPORT
575 if( sp
->subcodec
== DEFLATE_SUBCODEC_LIBDEFLATE
)
577 TIFFErrorExt(tif
->tif_clientdata
, module
,
578 "DeflateCodec = DEFLATE_SUBCODEC_LIBDEFLATE unsupported in this build");
585 return (*sp
->vsetparent
)(tif
, tag
, ap
);
591 ZIPVGetField(TIFF
* tif
, uint32_t tag
, va_list ap
)
593 ZIPState
* sp
= ZState(tif
);
596 case TIFFTAG_ZIPQUALITY
:
597 *va_arg(ap
, int*) = sp
->zipquality
;
600 case TIFFTAG_DEFLATE_SUBCODEC
:
601 *va_arg(ap
, int*) = sp
->subcodec
;
605 return (*sp
->vgetparent
)(tif
, tag
, ap
);
610 static const TIFFField zipFields
[] = {
611 { TIFFTAG_ZIPQUALITY
, 0, 0, TIFF_ANY
, 0, TIFF_SETGET_INT
, TIFF_SETGET_UNDEFINED
, FIELD_PSEUDO
, TRUE
, FALSE
, "", NULL
},
612 { TIFFTAG_DEFLATE_SUBCODEC
, 0, 0, TIFF_ANY
, 0, TIFF_SETGET_INT
, TIFF_SETGET_UNDEFINED
, FIELD_PSEUDO
, TRUE
, FALSE
, "", NULL
},
616 TIFFInitZIP(TIFF
* tif
, int scheme
)
618 static const char module
[] = "TIFFInitZIP";
621 assert( (scheme
== COMPRESSION_DEFLATE
)
622 || (scheme
== COMPRESSION_ADOBE_DEFLATE
));
628 * Merge codec-specific tag information.
630 if (!_TIFFMergeFields(tif
, zipFields
, TIFFArrayCount(zipFields
))) {
631 TIFFErrorExt(tif
->tif_clientdata
, module
,
632 "Merging Deflate codec-specific tags failed");
637 * Allocate state block so tag methods have storage to record values.
639 tif
->tif_data
= (uint8_t*) _TIFFcalloc(sizeof (ZIPState
), 1);
640 if (tif
->tif_data
== NULL
)
643 sp
->stream
.zalloc
= NULL
;
644 sp
->stream
.zfree
= NULL
;
645 sp
->stream
.opaque
= NULL
;
646 sp
->stream
.data_type
= Z_BINARY
;
649 * Override parent get/set field methods.
651 sp
->vgetparent
= tif
->tif_tagmethods
.vgetfield
;
652 tif
->tif_tagmethods
.vgetfield
= ZIPVGetField
; /* hook for codec tags */
653 sp
->vsetparent
= tif
->tif_tagmethods
.vsetfield
;
654 tif
->tif_tagmethods
.vsetfield
= ZIPVSetField
; /* hook for codec tags */
656 /* Default values for codec-specific fields */
657 sp
->zipquality
= Z_DEFAULT_COMPRESSION
; /* default comp. level */
659 #if LIBDEFLATE_SUPPORT
660 sp
->subcodec
= DEFLATE_SUBCODEC_LIBDEFLATE
;
662 sp
->subcodec
= DEFLATE_SUBCODEC_ZLIB
;
666 * Install codec methods.
668 tif
->tif_fixuptags
= ZIPFixupTags
;
669 tif
->tif_setupdecode
= ZIPSetupDecode
;
670 tif
->tif_predecode
= ZIPPreDecode
;
671 tif
->tif_decoderow
= ZIPDecode
;
672 tif
->tif_decodestrip
= ZIPDecode
;
673 tif
->tif_decodetile
= ZIPDecode
;
674 tif
->tif_setupencode
= ZIPSetupEncode
;
675 tif
->tif_preencode
= ZIPPreEncode
;
676 tif
->tif_postencode
= ZIPPostEncode
;
677 tif
->tif_encoderow
= ZIPEncode
;
678 tif
->tif_encodestrip
= ZIPEncode
;
679 tif
->tif_encodetile
= ZIPEncode
;
680 tif
->tif_cleanup
= ZIPCleanup
;
682 * Setup predictor setup.
684 (void) TIFFPredictorInit(tif
);
687 TIFFErrorExt(tif
->tif_clientdata
, module
,
688 "No space for ZIP state block");
691 #endif /* ZIP_SUPPORT */
693 /* vim: set ts=8 sts=8 sw=8 noet: */