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