]> git.saurik.com Git - wxWidgets.git/blob - src/common/unzip.c
1. small fix in wxDirDialog: SHBrowseForFolder() doesn't like '/'s
[wxWidgets.git] / src / common / unzip.c
1 /*
2
3 This file was altered for needs of wxWindows.
4 $Id$
5
6 */
7
8 /* unzip.c -- IO on .zip files using zlib
9 Version 0.15 beta, Mar 19th, 1998,
10
11 Read unzip.h for more info
12
13 */
14
15
16
17 #include "wx/setup.h"
18
19 #if wxUSE_ZLIB && wxUSE_ZIPSTREAM
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "zlib.h"
25
26 /* Not the right solution (paths in makefiles) but... */
27 #ifdef __BORLANDC__
28 #include "../common/unzip.h"
29 #else
30 #include "unzip.h"
31 #endif
32
33 #ifdef STDC
34 # include <stddef.h>
35 # include <string.h>
36 # include <stdlib.h>
37 #endif
38 #ifdef NO_ERRNO_H
39 extern int errno;
40 #else
41 # include <errno.h>
42 #endif
43
44
45 #ifndef local
46 # define local static
47 #endif
48 /* compile with -Dlocal if your debugger can't find static symbols */
49
50
51
52 #if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \
53 !defined(CASESENSITIVITYDEFAULT_NO)
54 #define CASESENSITIVITYDEFAULT_NO
55 #endif
56
57
58 #ifndef UNZ_BUFSIZE
59 #define UNZ_BUFSIZE (16384)
60 #endif
61
62 #ifndef UNZ_MAXFILENAMEINZIP
63 #define UNZ_MAXFILENAMEINZIP (256)
64 #endif
65
66 #ifndef ALLOC
67 # define ALLOC(size) (malloc(size))
68 #endif
69 #ifndef TRYFREE
70 # define TRYFREE(p) {if (p) free(p);}
71 #endif
72
73 #define SIZECENTRALDIRITEM (0x2e)
74 #define SIZEZIPLOCALHEADER (0x1e)
75
76
77 /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
78
79 #ifndef SEEK_CUR
80 #define SEEK_CUR 1
81 #endif
82
83 #ifndef SEEK_END
84 #define SEEK_END 2
85 #endif
86
87 #ifndef SEEK_SET
88 #define SEEK_SET 0
89 #endif
90
91 const char unz_copyright[] =
92 " unzip 0.15 Copyright 1998 Gilles Vollant ";
93
94 /* unz_file_info_interntal contain internal info about a file in zipfile*/
95 typedef struct unz_file_info_internal_s
96 {
97 uLong offset_curfile;/* relative offset of local header 4 bytes */
98 } unz_file_info_internal;
99
100
101 /* file_in_zip_read_info_s contain internal information about a file in zipfile,
102 when reading and decompress it */
103 typedef struct
104 {
105 char *read_buffer; /* internal buffer for compressed data */
106 z_stream stream; /* zLib stream structure for inflate */
107
108 uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
109 uLong stream_initialised; /* flag set if stream structure is initialised*/
110
111 uLong offset_local_extrafield;/* offset of the local extra field */
112 uInt size_local_extrafield;/* size of the local extra field */
113 uLong pos_local_extrafield; /* position in the local extra field in read*/
114
115 uLong crc32; /* crc32 of all data uncompressed */
116 uLong crc32_wait; /* crc32 we must obtain after decompress all */
117 uLong rest_read_compressed; /* number of byte to be decompressed */
118 uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
119 FILE* file; /* io structore of the zipfile */
120 uLong compression_method; /* compression method (0==store) */
121 uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
122 } file_in_zip_read_info_s;
123
124
125 /* unz_s contain internal information about the zipfile
126 */
127 typedef struct
128 {
129 FILE* file; /* io structore of the zipfile */
130 unz_global_info gi; /* public global information */
131 uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
132 uLong num_file; /* number of the current file in the zipfile*/
133 uLong pos_in_central_dir; /* pos of the current file in the central dir*/
134 uLong current_file_ok; /* flag about the usability of the current file*/
135 uLong central_pos; /* position of the beginning of the central dir*/
136
137 uLong size_central_dir; /* size of the central directory */
138 uLong offset_central_dir; /* offset of start of central directory with
139 respect to the starting disk number */
140
141 unz_file_info cur_file_info; /* public info about the current file in zip*/
142 unz_file_info_internal cur_file_info_internal; /* private info about it*/
143 file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
144 file if we are decompressing it */
145 } unz_s;
146
147 #if defined (__VISAGECPP__)
148 /* VA always requires prototypes */
149 int unzlocal_CheckCurrentFileCoherencyHeader (unz_s*, uInt*, uLong*, uInt*);
150 #endif
151
152 /* ===========================================================================
153 Read a byte from a gz_stream; update next_in and avail_in. Return EOF
154 for end of file.
155 IN assertion: the stream s has been sucessfully opened for reading.
156 */
157
158
159 local int unzlocal_getByte(fin,pi)
160 FILE *fin;
161 int *pi;
162 {
163 unsigned char c;
164 int err = fread(&c, 1, 1, fin);
165 if (err==1)
166 {
167 *pi = (int)c;
168 return UNZ_OK;
169 }
170 else
171 {
172 if (ferror(fin))
173 return UNZ_ERRNO;
174 else
175 return UNZ_EOF;
176 }
177 }
178
179
180 /* ===========================================================================
181 Reads a long in LSB order from the given gz_stream. Sets
182 */
183 local int unzlocal_getShort (fin,pX)
184 FILE* fin;
185 uLong *pX;
186 {
187 uLong x ;
188 int i;
189 int err;
190
191 err = unzlocal_getByte(fin,&i);
192 x = (uLong)i;
193
194 if (err==UNZ_OK)
195 err = unzlocal_getByte(fin,&i);
196 x += ((uLong)i)<<8;
197
198 if (err==UNZ_OK)
199 *pX = x;
200 else
201 *pX = 0;
202 return err;
203 }
204
205 local int unzlocal_getLong (fin,pX)
206 FILE* fin;
207 uLong *pX;
208 {
209 uLong x ;
210 int i;
211 int err;
212
213 err = unzlocal_getByte(fin,&i);
214 x = (uLong)i;
215
216 if (err==UNZ_OK)
217 err = unzlocal_getByte(fin,&i);
218 x += ((uLong)i)<<8;
219
220 if (err==UNZ_OK)
221 err = unzlocal_getByte(fin,&i);
222 x += ((uLong)i)<<16;
223
224 if (err==UNZ_OK)
225 err = unzlocal_getByte(fin,&i);
226 x += ((uLong)i)<<24;
227
228 if (err==UNZ_OK)
229 *pX = x;
230 else
231 *pX = 0;
232 return err;
233 }
234
235
236 /* My own strcmpi / strcasecmp */
237 local int strcmpcasenosensitive_internal (fileName1,fileName2)
238 const char* fileName1;
239 const char* fileName2;
240 {
241 for (;;)
242 {
243 char c1=*(fileName1++);
244 char c2=*(fileName2++);
245 if ((c1>='a') && (c1<='z'))
246 c1 -= 0x20;
247 if ((c2>='a') && (c2<='z'))
248 c2 -= 0x20;
249 if (c1=='\0')
250 return ((c2=='\0') ? 0 : -1);
251 if (c2=='\0')
252 return 1;
253 if (c1<c2)
254 return -1;
255 if (c1>c2)
256 return 1;
257 }
258 }
259
260
261 #ifdef CASESENSITIVITYDEFAULT_NO
262 #define CASESENSITIVITYDEFAULTVALUE 2
263 #else
264 #define CASESENSITIVITYDEFAULTVALUE 1
265 #endif
266
267 #ifndef STRCMPCASENOSENTIVEFUNCTION
268 #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
269 #endif
270
271 /*
272 Compare two filename (fileName1,fileName2).
273 If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
274 If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
275 or strcasecmp)
276 If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
277 (like 1 on Unix, 2 on Windows)
278
279 */
280 extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
281 const char* fileName1;
282 const char* fileName2;
283 int iCaseSensitivity;
284 {
285 if (iCaseSensitivity==0)
286 iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
287
288 if (iCaseSensitivity==1)
289 return strcmp(fileName1,fileName2);
290
291 return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
292 }
293
294 #define BUFREADCOMMENT (0x400)
295
296 /*
297 Locate the Central directory of a zipfile (at the end, just before
298 the global comment)
299 */
300 local uLong unzlocal_SearchCentralDir(fin)
301 FILE *fin;
302 {
303 unsigned char* buf;
304 uLong uSizeFile;
305 uLong uBackRead;
306 uLong uMaxBack=0xffff; /* maximum size of global comment */
307 uLong uPosFound=0;
308
309 if (fseek(fin,0,SEEK_END) != 0)
310 return 0;
311
312
313 uSizeFile = ftell( fin );
314
315 if (uMaxBack>uSizeFile)
316 uMaxBack = uSizeFile;
317
318 buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
319 if (buf==NULL)
320 return 0;
321
322 uBackRead = 4;
323 while (uBackRead<uMaxBack)
324 {
325 uLong uReadSize,uReadPos ;
326 int i;
327 if (uBackRead+BUFREADCOMMENT>uMaxBack)
328 uBackRead = uMaxBack;
329 else
330 uBackRead+=BUFREADCOMMENT;
331 uReadPos = uSizeFile-uBackRead ;
332
333 uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
334 (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
335 if (fseek(fin,uReadPos,SEEK_SET)!=0)
336 break;
337
338 if (fread(buf,(uInt)uReadSize,1,fin)!=1)
339 break;
340
341 for (i=(int)uReadSize-3; (i--)>0;)
342 if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
343 ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
344 {
345 uPosFound = uReadPos+i;
346 break;
347 }
348
349 if (uPosFound!=0)
350 break;
351 }
352 TRYFREE(buf);
353 return uPosFound;
354 }
355
356 #ifdef __WXMAC__
357 void wxUnix2MacFilename (char *s) ;
358 void
359 wxUnix2MacFilename (char *s)
360 {
361 if (s)
362 {
363 if ( *s == '.' )
364 {
365 /* relative path , since it goes on with slash which is translated to a : */
366 memmove( s , s+1 ,strlen( s ) ) ;
367 }
368 else if ( *s == '/' )
369 {
370 /* absolute path -> on mac just start with the drive name */
371 memmove( s , s+1 ,strlen( s ) ) ;
372 }
373 else
374 {
375 /* wxASSERT_MSG( 1 , "unkown path beginning" ) ; */
376 }
377 while (*s)
378 {
379 if (*s == '/' || *s == '\\')
380 {
381 /* convert any back-directory situations */
382 if ( *(s+1) == '.' && *(s+2) == '.' && ( (*(s+3) == '/' || *(s+3) == '\\') ) )
383 {
384 *s = ':';
385 memmove( s+1 , s+3 ,strlen( s+3 ) + 1 ) ;
386 }
387 else
388 *s = ':';
389 }
390
391 s++ ;
392 }
393 }
394 }
395 extern char * wxBuffer ;
396 #endif
397
398 /*
399 Open a Zip file. path contain the full pathname (by example,
400 on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer
401 "zlib/zlib109.zip".
402 If the zipfile cannot be opened (file don't exist or in not valid), the
403 return value is NULL.
404 Else, the return value is a unzFile Handle, usable with other function
405 of this unzip package.
406 */
407 extern unzFile ZEXPORT unzOpen (path)
408 const char *path;
409 {
410 unz_s us;
411 unz_s *s;
412 uLong central_pos,uL;
413 FILE * fin ;
414
415 uLong number_disk; /* number of the current dist, used for
416 spaning ZIP, unsupported, always 0*/
417 uLong number_disk_with_CD; /* number the the disk with central dir, used
418 for spaning ZIP, unsupported, always 0*/
419 uLong number_entry_CD; /* total number of entries in
420 the central dir
421 (same than number_entry on nospan) */
422
423 int err=UNZ_OK;
424
425 if (unz_copyright[0]!=' ')
426 return NULL;
427
428 #ifdef __WXMAC__
429 strcpy( wxBuffer , path ) ;
430 wxUnix2MacFilename( wxBuffer ) ;
431 fin=fopen(wxBuffer,"rb");
432 #else
433 fin=fopen(path,"rb");
434 #endif
435 if (fin==NULL)
436 return NULL;
437
438 central_pos = unzlocal_SearchCentralDir(fin);
439 if (central_pos==0)
440 err=UNZ_ERRNO;
441
442 if (fseek(fin,central_pos,SEEK_SET)!=0)
443 err=UNZ_ERRNO;
444
445 /* the signature, already checked */
446 if (unzlocal_getLong(fin,&uL)!=UNZ_OK)
447 err=UNZ_ERRNO;
448
449 /* number of this disk */
450 if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK)
451 err=UNZ_ERRNO;
452
453 /* number of the disk with the start of the central directory */
454 if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK)
455 err=UNZ_ERRNO;
456
457 /* total number of entries in the central dir on this disk */
458 if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK)
459 err=UNZ_ERRNO;
460
461 /* total number of entries in the central dir */
462 if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK)
463 err=UNZ_ERRNO;
464
465 if ((number_entry_CD!=us.gi.number_entry) ||
466 (number_disk_with_CD!=0) ||
467 (number_disk!=0))
468 err=UNZ_BADZIPFILE;
469
470 /* size of the central directory */
471 if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK)
472 err=UNZ_ERRNO;
473
474 /* offset of start of central directory with respect to the
475 starting disk number */
476 if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK)
477 err=UNZ_ERRNO;
478
479 /* zipfile comment length */
480 if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK)
481 err=UNZ_ERRNO;
482
483 if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
484 (err==UNZ_OK))
485 err=UNZ_BADZIPFILE;
486
487 if (err!=UNZ_OK)
488 {
489 fclose(fin);
490 return NULL;
491 }
492 us.file=fin;
493 us.byte_before_the_zipfile = central_pos -
494 (us.offset_central_dir+us.size_central_dir);
495 us.central_pos = central_pos;
496 us.pfile_in_zip_read = NULL;
497
498
499 s=(unz_s*)ALLOC(sizeof(unz_s));
500 *s=us;
501 unzGoToFirstFile((unzFile)s);
502 return (unzFile)s;
503 }
504
505
506 /*
507 Close a ZipFile opened with unzipOpen.
508 If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
509 these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
510 return UNZ_OK if there is no problem. */
511 extern int ZEXPORT unzClose (file)
512 unzFile file;
513 {
514 unz_s* s;
515 if (file==NULL)
516 return UNZ_PARAMERROR;
517 s=(unz_s*)file;
518
519 if (s->pfile_in_zip_read!=NULL)
520 unzCloseCurrentFile(file);
521
522 fclose(s->file);
523 TRYFREE(s);
524 return UNZ_OK;
525 }
526
527
528 /*
529 Write info about the ZipFile in the *pglobal_info structure.
530 No preparation of the structure is needed
531 return UNZ_OK if there is no problem. */
532 extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
533 unzFile file;
534 unz_global_info *pglobal_info;
535 {
536 unz_s* s;
537 if (file==NULL)
538 return UNZ_PARAMERROR;
539 s=(unz_s*)file;
540 *pglobal_info=s->gi;
541 return UNZ_OK;
542 }
543
544
545 /*
546 Translate date/time from Dos format to tm_unz (readable more easilty)
547 */
548 local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
549 uLong ulDosDate;
550 tm_unz* ptm;
551 {
552 uLong uDate;
553 uDate = (uLong)(ulDosDate>>16);
554 ptm->tm_mday = (uInt)(uDate&0x1f) ;
555 ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ;
556 ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
557
558 ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
559 ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ;
560 ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ;
561 }
562
563 /*
564 Get Info about the current file in the zipfile, with internal only info
565 */
566 local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
567 unz_file_info *pfile_info,
568 unz_file_info_internal
569 *pfile_info_internal,
570 char *szFileName,
571 uLong fileNameBufferSize,
572 void *extraField,
573 uLong extraFieldBufferSize,
574 char *szComment,
575 uLong commentBufferSize));
576
577 local int unzlocal_GetCurrentFileInfoInternal (file,
578 pfile_info,
579 pfile_info_internal,
580 szFileName, fileNameBufferSize,
581 extraField, extraFieldBufferSize,
582 szComment, commentBufferSize)
583 unzFile file;
584 unz_file_info *pfile_info;
585 unz_file_info_internal *pfile_info_internal;
586 char *szFileName;
587 uLong fileNameBufferSize;
588 void *extraField;
589 uLong extraFieldBufferSize;
590 char *szComment;
591 uLong commentBufferSize;
592 {
593 unz_s* s;
594 unz_file_info file_info;
595 unz_file_info_internal file_info_internal;
596 int err=UNZ_OK;
597 uLong uMagic;
598 long lSeek=0;
599
600 if (file==NULL)
601 return UNZ_PARAMERROR;
602 s=(unz_s*)file;
603 if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0)
604 err=UNZ_ERRNO;
605
606
607 /* we check the magic */
608 if (err==UNZ_OK)
609 if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK)
610 err=UNZ_ERRNO;
611 else if (uMagic!=0x02014b50)
612 err=UNZ_BADZIPFILE;
613
614 if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK)
615 err=UNZ_ERRNO;
616
617 if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK)
618 err=UNZ_ERRNO;
619
620 if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK)
621 err=UNZ_ERRNO;
622
623 if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK)
624 err=UNZ_ERRNO;
625
626 if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK)
627 err=UNZ_ERRNO;
628
629 unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
630
631 if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK)
632 err=UNZ_ERRNO;
633
634 if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK)
635 err=UNZ_ERRNO;
636
637 if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK)
638 err=UNZ_ERRNO;
639
640 if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK)
641 err=UNZ_ERRNO;
642
643 if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK)
644 err=UNZ_ERRNO;
645
646 if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK)
647 err=UNZ_ERRNO;
648
649 if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK)
650 err=UNZ_ERRNO;
651
652 if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK)
653 err=UNZ_ERRNO;
654
655 if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK)
656 err=UNZ_ERRNO;
657
658 if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK)
659 err=UNZ_ERRNO;
660
661 lSeek+=file_info.size_filename;
662 if ((err==UNZ_OK) && (szFileName!=NULL))
663 {
664 uLong uSizeRead ;
665 if (file_info.size_filename<fileNameBufferSize)
666 {
667 *(szFileName+file_info.size_filename)='\0';
668 uSizeRead = file_info.size_filename;
669 }
670 else
671 uSizeRead = fileNameBufferSize;
672
673 if ((file_info.size_filename>0) && (fileNameBufferSize>0))
674 if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1)
675 err=UNZ_ERRNO;
676 lSeek -= uSizeRead;
677 }
678
679
680 if ((err==UNZ_OK) && (extraField!=NULL))
681 {
682 uLong uSizeRead ;
683 if (file_info.size_file_extra<extraFieldBufferSize)
684 uSizeRead = file_info.size_file_extra;
685 else
686 uSizeRead = extraFieldBufferSize;
687
688 if (lSeek!=0)
689 if (fseek(s->file,lSeek,SEEK_CUR)==0)
690 lSeek=0;
691 else
692 err=UNZ_ERRNO;
693 if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
694 if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1)
695 err=UNZ_ERRNO;
696 lSeek += file_info.size_file_extra - uSizeRead;
697 }
698 else
699 lSeek+=file_info.size_file_extra;
700
701
702 if ((err==UNZ_OK) && (szComment!=NULL))
703 {
704 uLong uSizeRead ;
705 if (file_info.size_file_comment<commentBufferSize)
706 {
707 *(szComment+file_info.size_file_comment)='\0';
708 uSizeRead = file_info.size_file_comment;
709 }
710 else
711 uSizeRead = commentBufferSize;
712
713 if (lSeek!=0)
714 if (fseek(s->file,lSeek,SEEK_CUR)==0)
715 lSeek=0;
716 else
717 err=UNZ_ERRNO;
718 if ((file_info.size_file_comment>0) && (commentBufferSize>0))
719 if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1)
720 err=UNZ_ERRNO;
721 lSeek+=file_info.size_file_comment - uSizeRead;
722 }
723 else
724 lSeek+=file_info.size_file_comment;
725
726 if ((err==UNZ_OK) && (pfile_info!=NULL))
727 *pfile_info=file_info;
728
729 if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
730 *pfile_info_internal=file_info_internal;
731
732 return err;
733 }
734
735
736
737 /*
738 Write info about the ZipFile in the *pglobal_info structure.
739 No preparation of the structure is needed
740 return UNZ_OK if there is no problem.
741 */
742 extern int ZEXPORT unzGetCurrentFileInfo (file,
743 pfile_info,
744 szFileName, fileNameBufferSize,
745 extraField, extraFieldBufferSize,
746 szComment, commentBufferSize)
747 unzFile file;
748 unz_file_info *pfile_info;
749 char *szFileName;
750 uLong fileNameBufferSize;
751 void *extraField;
752 uLong extraFieldBufferSize;
753 char *szComment;
754 uLong commentBufferSize;
755 {
756 return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
757 szFileName,fileNameBufferSize,
758 extraField,extraFieldBufferSize,
759 szComment,commentBufferSize);
760 }
761
762 /*
763 Set the current file of the zipfile to the first file.
764 return UNZ_OK if there is no problem
765 */
766 extern int ZEXPORT unzGoToFirstFile (file)
767 unzFile file;
768 {
769 int err=UNZ_OK;
770 unz_s* s;
771 if (file==NULL)
772 return UNZ_PARAMERROR;
773 s=(unz_s*)file;
774 s->pos_in_central_dir=s->offset_central_dir;
775 s->num_file=0;
776 err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
777 &s->cur_file_info_internal,
778 NULL,0,NULL,0,NULL,0);
779 s->current_file_ok = (err == UNZ_OK);
780 return err;
781 }
782
783
784 /*
785 Set the current file of the zipfile to the next file.
786 return UNZ_OK if there is no problem
787 return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
788 */
789 extern int ZEXPORT unzGoToNextFile (file)
790 unzFile file;
791 {
792 unz_s* s;
793 int err;
794
795 if (file==NULL)
796 return UNZ_PARAMERROR;
797 s=(unz_s*)file;
798 if (!s->current_file_ok)
799 return UNZ_END_OF_LIST_OF_FILE;
800 if (s->num_file+1==s->gi.number_entry)
801 return UNZ_END_OF_LIST_OF_FILE;
802
803 s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
804 s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
805 s->num_file++;
806 err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
807 &s->cur_file_info_internal,
808 NULL,0,NULL,0,NULL,0);
809 s->current_file_ok = (err == UNZ_OK);
810 return err;
811 }
812
813
814 /*
815 Try locate the file szFileName in the zipfile.
816 For the iCaseSensitivity signification, see unzipStringFileNameCompare
817
818 return value :
819 UNZ_OK if the file is found. It becomes the current file.
820 UNZ_END_OF_LIST_OF_FILE if the file is not found
821 */
822 extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
823 unzFile file;
824 const char *szFileName;
825 int iCaseSensitivity;
826 {
827 unz_s* s;
828 int err;
829
830
831 uLong num_fileSaved;
832 uLong pos_in_central_dirSaved;
833
834
835 if (file==NULL)
836 return UNZ_PARAMERROR;
837
838 if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
839 return UNZ_PARAMERROR;
840
841 s=(unz_s*)file;
842 if (!s->current_file_ok)
843 return UNZ_END_OF_LIST_OF_FILE;
844
845 num_fileSaved = s->num_file;
846 pos_in_central_dirSaved = s->pos_in_central_dir;
847
848 err = unzGoToFirstFile(file);
849
850 while (err == UNZ_OK)
851 {
852 char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
853 unzGetCurrentFileInfo(file,NULL,
854 szCurrentFileName,sizeof(szCurrentFileName)-1,
855 NULL,0,NULL,0);
856 if (unzStringFileNameCompare(szCurrentFileName,
857 szFileName,iCaseSensitivity)==0)
858 return UNZ_OK;
859 err = unzGoToNextFile(file);
860 }
861
862 s->num_file = num_fileSaved ;
863 s->pos_in_central_dir = pos_in_central_dirSaved ;
864 return err;
865 }
866
867
868 /*
869 Read the local header of the current zipfile
870 Check the coherency of the local header and info in the end of central
871 directory about this file
872 store in *piSizeVar the size of extra info in local header
873 (filename and size of extra field data)
874 */
875 local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
876 poffset_local_extrafield,
877 psize_local_extrafield)
878 unz_s* s;
879 uInt* piSizeVar;
880 uLong *poffset_local_extrafield;
881 uInt *psize_local_extrafield;
882 {
883 uLong uMagic,uData,uFlags;
884 uLong size_filename;
885 uLong size_extra_field;
886 int err=UNZ_OK;
887
888 *piSizeVar = 0;
889 *poffset_local_extrafield = 0;
890 *psize_local_extrafield = 0;
891
892 if (fseek(s->file,s->cur_file_info_internal.offset_curfile +
893 s->byte_before_the_zipfile,SEEK_SET)!=0)
894 return UNZ_ERRNO;
895
896
897 if (err==UNZ_OK)
898 if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK)
899 err=UNZ_ERRNO;
900 else if (uMagic!=0x04034b50)
901 err=UNZ_BADZIPFILE;
902
903 if (unzlocal_getShort(s->file,&uData) != UNZ_OK)
904 err=UNZ_ERRNO;
905 /*
906 else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
907 err=UNZ_BADZIPFILE;
908 */
909 if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK)
910 err=UNZ_ERRNO;
911
912 if (unzlocal_getShort(s->file,&uData) != UNZ_OK)
913 err=UNZ_ERRNO;
914 else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
915 err=UNZ_BADZIPFILE;
916
917 if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
918 (s->cur_file_info.compression_method!=Z_DEFLATED))
919 err=UNZ_BADZIPFILE;
920
921 if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */
922 err=UNZ_ERRNO;
923
924 if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */
925 err=UNZ_ERRNO;
926 else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
927 ((uFlags & 8)==0))
928 err=UNZ_BADZIPFILE;
929
930 if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */
931 err=UNZ_ERRNO;
932 else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
933 ((uFlags & 8)==0))
934 err=UNZ_BADZIPFILE;
935
936 if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */
937 err=UNZ_ERRNO;
938 else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) &&
939 ((uFlags & 8)==0))
940 err=UNZ_BADZIPFILE;
941
942
943 if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK)
944 err=UNZ_ERRNO;
945 else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
946 err=UNZ_BADZIPFILE;
947
948 *piSizeVar += (uInt)size_filename;
949
950 if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK)
951 err=UNZ_ERRNO;
952 *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
953 SIZEZIPLOCALHEADER + size_filename;
954 *psize_local_extrafield = (uInt)size_extra_field;
955
956 *piSizeVar += (uInt)size_extra_field;
957
958 return err;
959 }
960
961 /*
962 Open for reading data the current file in the zipfile.
963 If there is no error and the file is opened, the return value is UNZ_OK.
964 */
965 extern int ZEXPORT unzOpenCurrentFile (file)
966 unzFile file;
967 {
968 int err=UNZ_OK;
969 int Store;
970 uInt iSizeVar;
971 unz_s* s;
972 file_in_zip_read_info_s* pfile_in_zip_read_info;
973 uLong offset_local_extrafield; /* offset of the local extra field */
974 uInt size_local_extrafield; /* size of the local extra field */
975
976 if (file==NULL)
977 return UNZ_PARAMERROR;
978 s=(unz_s*)file;
979 if (!s->current_file_ok)
980 return UNZ_PARAMERROR;
981
982 if (s->pfile_in_zip_read != NULL)
983 unzCloseCurrentFile(file);
984
985 if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
986 &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
987 return UNZ_BADZIPFILE;
988
989 pfile_in_zip_read_info = (file_in_zip_read_info_s*)
990 ALLOC(sizeof(file_in_zip_read_info_s));
991 if (pfile_in_zip_read_info==NULL)
992 return UNZ_INTERNALERROR;
993
994 pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
995 pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
996 pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
997 pfile_in_zip_read_info->pos_local_extrafield=0;
998
999 if (pfile_in_zip_read_info->read_buffer==NULL)
1000 {
1001 TRYFREE(pfile_in_zip_read_info);
1002 return UNZ_INTERNALERROR;
1003 }
1004
1005 pfile_in_zip_read_info->stream_initialised=0;
1006
1007 if ((s->cur_file_info.compression_method!=0) &&
1008 (s->cur_file_info.compression_method!=Z_DEFLATED))
1009 err=UNZ_BADZIPFILE;
1010 Store = s->cur_file_info.compression_method==0;
1011
1012 pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
1013 pfile_in_zip_read_info->crc32=0;
1014 pfile_in_zip_read_info->compression_method =
1015 s->cur_file_info.compression_method;
1016 pfile_in_zip_read_info->file=s->file;
1017 pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
1018
1019 pfile_in_zip_read_info->stream.total_out = 0;
1020
1021 if (!Store)
1022 {
1023 pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
1024 pfile_in_zip_read_info->stream.zfree = (free_func)0;
1025 pfile_in_zip_read_info->stream.opaque = (voidpf)0;
1026
1027 err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
1028 if (err == Z_OK)
1029 pfile_in_zip_read_info->stream_initialised=1;
1030 /* windowBits is passed < 0 to tell that there is no zlib header.
1031 * Note that in this case inflate *requires* an extra "dummy" byte
1032 * after the compressed stream in order to complete decompression and
1033 * return Z_STREAM_END.
1034 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1035 * size of both compressed and uncompressed data
1036 */
1037 }
1038 pfile_in_zip_read_info->rest_read_compressed =
1039 s->cur_file_info.compressed_size ;
1040 pfile_in_zip_read_info->rest_read_uncompressed =
1041 s->cur_file_info.uncompressed_size ;
1042
1043
1044 pfile_in_zip_read_info->pos_in_zipfile =
1045 s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
1046 iSizeVar;
1047
1048 pfile_in_zip_read_info->stream.avail_in = (uInt)0;
1049
1050
1051 s->pfile_in_zip_read = pfile_in_zip_read_info;
1052 return UNZ_OK;
1053 }
1054
1055
1056 /*
1057 Read bytes from the current file.
1058 buf contain buffer where data must be copied
1059 len the size of buf.
1060
1061 return the number of byte copied if somes bytes are copied
1062 return 0 if the end of file was reached
1063 return <0 with error code if there is an error
1064 (UNZ_ERRNO for IO error, or zLib error for uncompress error)
1065 */
1066 extern int ZEXPORT unzReadCurrentFile (file, buf, len)
1067 unzFile file;
1068 voidp buf;
1069 unsigned len;
1070 {
1071 int err=UNZ_OK;
1072 uInt iRead = 0;
1073 unz_s* s;
1074 file_in_zip_read_info_s* pfile_in_zip_read_info;
1075 if (file==NULL)
1076 return UNZ_PARAMERROR;
1077 s=(unz_s*)file;
1078 pfile_in_zip_read_info=s->pfile_in_zip_read;
1079
1080 if (pfile_in_zip_read_info==NULL)
1081 return UNZ_PARAMERROR;
1082
1083
1084 if ((pfile_in_zip_read_info->read_buffer == NULL))
1085 return UNZ_END_OF_LIST_OF_FILE;
1086 if (len==0)
1087 return 0;
1088
1089 pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
1090
1091 pfile_in_zip_read_info->stream.avail_out = (uInt)len;
1092
1093 if (len>pfile_in_zip_read_info->rest_read_uncompressed)
1094 pfile_in_zip_read_info->stream.avail_out =
1095 (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
1096
1097 while (pfile_in_zip_read_info->stream.avail_out>0)
1098 {
1099 if ((pfile_in_zip_read_info->stream.avail_in==0) &&
1100 (pfile_in_zip_read_info->rest_read_compressed>0))
1101 {
1102 uInt uReadThis = UNZ_BUFSIZE;
1103 if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
1104 uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
1105 if (uReadThis == 0)
1106 return UNZ_EOF;
1107 if (fseek(pfile_in_zip_read_info->file,
1108 pfile_in_zip_read_info->pos_in_zipfile +
1109 pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0)
1110 return UNZ_ERRNO;
1111 if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1,
1112 pfile_in_zip_read_info->file)!=1)
1113 return UNZ_ERRNO;
1114 pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
1115
1116 pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
1117
1118 pfile_in_zip_read_info->stream.next_in =
1119 (Bytef*)pfile_in_zip_read_info->read_buffer;
1120 pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
1121 }
1122
1123 if (pfile_in_zip_read_info->compression_method==0)
1124 {
1125 uInt uDoCopy,i ;
1126 if (pfile_in_zip_read_info->stream.avail_out <
1127 pfile_in_zip_read_info->stream.avail_in)
1128 uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
1129 else
1130 uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
1131
1132 for (i=0;i<uDoCopy;i++)
1133 *(pfile_in_zip_read_info->stream.next_out+i) =
1134 *(pfile_in_zip_read_info->stream.next_in+i);
1135
1136 pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
1137 pfile_in_zip_read_info->stream.next_out,
1138 uDoCopy);
1139 pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
1140 pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
1141 pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
1142 pfile_in_zip_read_info->stream.next_out += uDoCopy;
1143 pfile_in_zip_read_info->stream.next_in += uDoCopy;
1144 pfile_in_zip_read_info->stream.total_out += uDoCopy;
1145 iRead += uDoCopy;
1146 }
1147 else
1148 {
1149 uLong uTotalOutBefore,uTotalOutAfter;
1150 const Bytef *bufBefore;
1151 uLong uOutThis;
1152 int flush=Z_SYNC_FLUSH;
1153
1154 uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
1155 bufBefore = pfile_in_zip_read_info->stream.next_out;
1156
1157 /*
1158 if ((pfile_in_zip_read_info->rest_read_uncompressed ==
1159 pfile_in_zip_read_info->stream.avail_out) &&
1160 (pfile_in_zip_read_info->rest_read_compressed == 0))
1161 flush = Z_FINISH;
1162 */
1163 err=inflate(&pfile_in_zip_read_info->stream,flush);
1164
1165 uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
1166 uOutThis = uTotalOutAfter-uTotalOutBefore;
1167
1168 pfile_in_zip_read_info->crc32 =
1169 crc32(pfile_in_zip_read_info->crc32,bufBefore,
1170 (uInt)(uOutThis));
1171
1172 pfile_in_zip_read_info->rest_read_uncompressed -=
1173 uOutThis;
1174
1175 iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
1176
1177 if (err==Z_STREAM_END)
1178 return (iRead==0) ? UNZ_EOF : iRead;
1179 if (err!=Z_OK)
1180 break;
1181 }
1182 }
1183
1184 if (err==Z_OK)
1185 return iRead;
1186 return err;
1187 }
1188
1189
1190 /*
1191 Give the current position in uncompressed data
1192 */
1193 extern z_off_t ZEXPORT unztell (file)
1194 unzFile file;
1195 {
1196 unz_s* s;
1197 file_in_zip_read_info_s* pfile_in_zip_read_info;
1198 if (file==NULL)
1199 return UNZ_PARAMERROR;
1200 s=(unz_s*)file;
1201 pfile_in_zip_read_info=s->pfile_in_zip_read;
1202
1203 if (pfile_in_zip_read_info==NULL)
1204 return UNZ_PARAMERROR;
1205
1206 return (z_off_t)pfile_in_zip_read_info->stream.total_out;
1207 }
1208
1209
1210 /*
1211 return 1 if the end of file was reached, 0 elsewhere
1212 */
1213 extern int ZEXPORT unzeof (file)
1214 unzFile file;
1215 {
1216 unz_s* s;
1217 file_in_zip_read_info_s* pfile_in_zip_read_info;
1218 if (file==NULL)
1219 return UNZ_PARAMERROR;
1220 s=(unz_s*)file;
1221 pfile_in_zip_read_info=s->pfile_in_zip_read;
1222
1223 if (pfile_in_zip_read_info==NULL)
1224 return UNZ_PARAMERROR;
1225
1226 if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
1227 return 1;
1228 else
1229 return 0;
1230 }
1231
1232
1233
1234 /*
1235 Read extra field from the current file (opened by unzOpenCurrentFile)
1236 This is the local-header version of the extra field (sometimes, there is
1237 more info in the local-header version than in the central-header)
1238
1239 if buf==NULL, it return the size of the local extra field that can be read
1240
1241 if buf!=NULL, len is the size of the buffer, the extra header is copied in
1242 buf.
1243 the return value is the number of bytes copied in buf, or (if <0)
1244 the error code
1245 */
1246 extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
1247 unzFile file;
1248 voidp buf;
1249 unsigned len;
1250 {
1251 unz_s* s;
1252 file_in_zip_read_info_s* pfile_in_zip_read_info;
1253 uInt read_now;
1254 uLong size_to_read;
1255
1256 if (file==NULL)
1257 return UNZ_PARAMERROR;
1258 s=(unz_s*)file;
1259 pfile_in_zip_read_info=s->pfile_in_zip_read;
1260
1261 if (pfile_in_zip_read_info==NULL)
1262 return UNZ_PARAMERROR;
1263
1264 size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
1265 pfile_in_zip_read_info->pos_local_extrafield);
1266
1267 if (buf==NULL)
1268 return (int)size_to_read;
1269
1270 if (len>size_to_read)
1271 read_now = (uInt)size_to_read;
1272 else
1273 read_now = (uInt)len ;
1274
1275 if (read_now==0)
1276 return 0;
1277
1278 if (fseek(pfile_in_zip_read_info->file,
1279 pfile_in_zip_read_info->offset_local_extrafield +
1280 pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0)
1281 return UNZ_ERRNO;
1282
1283 if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1)
1284 return UNZ_ERRNO;
1285
1286 return (int)read_now;
1287 }
1288
1289 /*
1290 Close the file in zip opened with unzipOpenCurrentFile
1291 Return UNZ_CRCERROR if all the file was read but the CRC is not good
1292 */
1293 extern int ZEXPORT unzCloseCurrentFile (file)
1294 unzFile file;
1295 {
1296 int err=UNZ_OK;
1297
1298 unz_s* s;
1299 file_in_zip_read_info_s* pfile_in_zip_read_info;
1300 if (file==NULL)
1301 return UNZ_PARAMERROR;
1302 s=(unz_s*)file;
1303 pfile_in_zip_read_info=s->pfile_in_zip_read;
1304
1305 if (pfile_in_zip_read_info==NULL)
1306 return UNZ_PARAMERROR;
1307
1308
1309 if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
1310 {
1311 if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
1312 err=UNZ_CRCERROR;
1313 }
1314
1315
1316 TRYFREE(pfile_in_zip_read_info->read_buffer);
1317 pfile_in_zip_read_info->read_buffer = NULL;
1318 if (pfile_in_zip_read_info->stream_initialised)
1319 inflateEnd(&pfile_in_zip_read_info->stream);
1320
1321 pfile_in_zip_read_info->stream_initialised = 0;
1322 TRYFREE(pfile_in_zip_read_info);
1323
1324 s->pfile_in_zip_read=NULL;
1325
1326 return err;
1327 }
1328
1329
1330 /*
1331 Get the global comment string of the ZipFile, in the szComment buffer.
1332 uSizeBuf is the size of the szComment buffer.
1333 return the number of byte copied or an error code <0
1334 */
1335 extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
1336 unzFile file;
1337 char *szComment;
1338 uLong uSizeBuf;
1339 {
1340 int err=UNZ_OK;
1341 unz_s* s;
1342 uLong uReadThis ;
1343 if (file==NULL)
1344 return UNZ_PARAMERROR;
1345 s=(unz_s*)file;
1346
1347 uReadThis = uSizeBuf;
1348 if (uReadThis>s->gi.size_comment)
1349 uReadThis = s->gi.size_comment;
1350
1351 if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0)
1352 return UNZ_ERRNO;
1353
1354 if (uReadThis>0)
1355 {
1356 *szComment='\0';
1357 if (fread(szComment,(uInt)uReadThis,1,s->file)!=1)
1358 return UNZ_ERRNO;
1359 }
1360
1361 if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
1362 *(szComment+s->gi.size_comment)='\0';
1363 return (int)uReadThis;
1364 }
1365
1366 #else
1367
1368 /* the file shouldn't be empty, som compilers don't like it */
1369 static const int dummyVariableInUnzip = 17;
1370
1371 #endif /* wxUSE_ZLIB && wxUSE_ZIPSTREAM */