diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/zziplib/zzip/fseeko.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/zziplib/zzip/fseeko.c | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/zziplib/zzip/fseeko.c b/Build/source/texk/web2c/luatexdir/zziplib/zzip/fseeko.c new file mode 100644 index 00000000000..2207f5dc981 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/zziplib/zzip/fseeko.c @@ -0,0 +1,580 @@ +/* + * NOTE: this is part of libzzipfseeko (i.e. it is not libzzip). + * + * These routines are fully independent from the traditional zzip + * implementation. They assume a readonly seekable stdio handle + * representing a complete zip file. The functions show how to + * parse the structure, find files and return a decoded bytestream. + * + * These routines are a bit simple and really here for documenting + * the way to access a zip file. The complexity of zip access comes + * from staggered reading of bytes and reposition of a filepointer in + * a big archive with lots of files and long compressed datastreams. + * Plus varaints of drop-in stdio replacements, obfuscation routines, + * auto fileextensions, drop-in dirent replacements, and so on... + * + * btw, we can _not_ use fgetpos/fsetpos since an fpos_t has no asserted + * relation to a linear seek value as specified in zip info headers. In + * general it is not a problem if your system has no fseeko/ftello pair + * since we can fallback to fseek/ftell which limits the zip disk size + * to 2MiBs but the zip-storable seek values are 32bit limited anyway. + * + * Author: + * Guido Draheim <guidod@gmx.de> + * + * Copyright (c) 2003,2004 Guido Draheim + * All rights reserved, + * use under the restrictions of the + * Lesser GNU General Public License + * or alternatively the restrictions + * of the Mozilla Public License 1.1 + */ + +#define _LARGEFILE_SOURCE 1 +#define _ZZIP_ENTRY_STRUCT 1 + +#include <zzip/types.h> + +#include <assert.h> +#include <stdlib.h> +#include <sys/stat.h> + +#if defined ZZIP_HAVE_STRING_H +#include <string.h> +#elif defined ZZIP_HAVE_STRINGS_H +#include <strings.h> +#endif + +#include <zlib.h> +#include <zzip/format.h> +#include <zzip/fseeko.h> +#include <zzip/fetch.h> +#include <zzip/__mmap.h> +#include <zzip/__fnmatch.h> + +#if __STDC_VERSION__+0 > 199900L +#define ___ +#define ____ +#else +#define ___ { +#define ____ } +#endif + +#ifndef ZZIP_HAVE_FSEEKO +#define fseeko fseek +#define ftello ftello +#endif + +/* note that the struct zzip_entry inherits the zzip_disk_entry values + * and usually carries a copy of its values (in disk format!). To make the + * following code more readable, we use a shorthand notation for the + * upcast needed in C (not needed in C++) as "disk_(entry)". + */ +#ifdef __zzip_entry_extends_zzip_disk_entry +#define disk_(_entry_) _entry_ +#else +#define disk_(_entry_) (& (_entry_)->head) +#endif + +/* we try to round all seeks to the pagesize - since we do not use + * the sys/mmap interface we have to guess a good value here: */ +#define PAGESIZE 8192 + +/* ====================================================================== */ +/* helper functions */ + +/** => zzip_entry_data_offset + * This functions read the correspoding struct zzip_file_header from + * the zip disk of the given "entry". The returned off_t points to the + * end of the file_header where the current fseek pointer has stopped. + * This is used to immediatly parse out any filename/extras block following + * the file_header. The return value is null on error. + */ +static zzip_off_t +zzip_entry_fread_file_header (ZZIP_ENTRY* entry, + struct zzip_file_header* file_header) +{ + if (! entry || ! file_header) return 0; + ___ zzip_off_t offset = zzip_disk_entry_fileoffset (disk_(entry)); + if (0 > offset || offset >= entry->disksize) return 0; + + fseeko (entry->diskfile, offset, SEEK_SET); + return (fread (file_header, sizeof(*file_header), 1, entry->diskfile) + ? offset+sizeof(*file_header) : 0 ); ____; +} + +/** helper functions for (fseeko) zip access api + * + * This functions returns the seekval offset of the data portion of the + * file referenced by the given zzip_entry. It requires an intermediate + * check of the file_header structure (i.e. it reads it from disk). After + * this call, the contained diskfile readposition is already set to the + * data_offset returned here. On error -1 is returned. + */ +zzip_off_t +zzip_entry_data_offset(ZZIP_ENTRY* entry) +{ + struct zzip_file_header file_header; + if (! entry) return -1; + ___ zzip_off_t offset = + zzip_entry_fread_file_header (entry, & file_header); + if (! offset) return -1; + offset += zzip_file_header_sizeof_tails (& file_header); + fseeko (entry->diskfile, offset, SEEK_SET); + return offset; ____; +} + +/** => zzip_entry_data_offset + * This function is a big helper despite its little name: in a zip file the + * encoded filenames are usually NOT zero-terminated but for common usage + * with libc we need it that way. Secondly, the filename SHOULD be present + * in the zip central directory but if not then we fallback to the filename + * given in the file_header of each compressed data portion. + */ +zzip__new__ char* +zzip_entry_strdup_name(ZZIP_ENTRY* entry) +{ + if (! entry) return 0; + + ___ zzip_size_t len; + if ((len = zzip_disk_entry_namlen (disk_(entry)))) { + char* name = malloc (len+1); + if (! name) return 0; + memcpy (name, entry->tail, len); + name[len] = '\0'; + return name; + } + ___ auto struct zzip_file_header header; + if (zzip_entry_fread_file_header (entry, &header) + && ( len = zzip_file_header_namlen(&header) )) { + char* name = malloc (len+1); + if (! name) return 0; + fread (name, 1, len, entry->diskfile); + name[len] = '\0'; + return name; + } + return 0; + ____;____; +} + +static int +prescan_entry(ZZIP_ENTRY* entry) +{ + assert (entry); + ___ zzip_off_t tailsize = zzip_disk_entry_sizeof_tails (disk_(entry)); + if (tailsize+1 > entry->tailalloc) { + char* newtail = realloc (entry->tail, tailsize+1); + if (! newtail) return ENOMEM; + entry->tail = newtail; + entry->tailalloc = tailsize+1; + } + fread (entry->tail, 1, tailsize, entry->diskfile); + /* name + comment + extras */ + return 0; ____; +} + +static void +prescan_clear(ZZIP_ENTRY* entry) +{ + assert (entry); + if (entry->tail) free (entry->tail); + entry->tail = 0; entry->tailalloc = 0; +} + +/* ====================================================================== */ + +/** => zzip_entry_findfile + * + * This function is the first call of all the zip access functions here. + * It contains the code to find the first entry of the zip central directory. + * Here we require the stdio handle to represent a real zip file where the + * disk_trailer is _last_ in the file area, so that its position would be at + * a fixed offset from the end of the file area if not for the comment field + * allowed to be of variable length (which needs us to do a little search + * for the disk_tailer). However, in this simple implementation we disregard + * any disk_trailer info telling about multidisk archives, so we just return + * a pointer to the first entry in the zip central directory of that file. + * + * For an actual means, we are going to search backwards from the end + * of the mmaped block looking for the PK-magic signature of a + * disk_trailer. If we see one then we check the rootseek value to + * find the first disk_entry of the root central directory. If we find + * the correct PK-magic signature of a disk_entry over there then we + * assume we are done and we are going to return a pointer to that label. + * + * The return value is a pointer to the first zzip_disk_entry being checked + * to be within the bounds of the file area specified by the arguments. If + * no disk_trailer was found then null is returned, and likewise we only + * accept a disk_trailer with a seekvalue that points to a disk_entry and + * both parts have valid PK-magic parts. Beyond some sanity check we try to + * catch a common brokeness with zip archives that still allows us to find + * the start of the zip central directory. + */ +zzip__new__ ZZIP_ENTRY* +zzip_entry_findfirst(FILE* disk) +{ + if (! disk) return 0; + fseeko (disk, 0, SEEK_END); + ___ zzip_off_t disksize = ftello (disk); + if (disksize < (zzip_off_t) sizeof(struct zzip_disk_trailer)) return 0; + /* we read out chunks of 8 KiB in the hope to match disk granularity */ + ___ zzip_off_t pagesize = PAGESIZE; /* getpagesize() */ + ___ ZZIP_ENTRY* entry = malloc (sizeof(*entry)); if (! entry) return 0; + ___ unsigned char* buffer = malloc (pagesize); if (! buffer) goto nomem; + + assert (pagesize/2 > (zzip_off_t) sizeof (struct zzip_disk_trailer)); + /* at each step, we will fread a pagesize block which overlaps with the + * previous read by means of pagesize/2 step at the end of the while(1) */ + ___ zzip_off_t mapoffs = disksize &~ (pagesize-1); + ___ zzip_off_t mapsize = disksize - mapoffs; + if (mapoffs && mapsize < pagesize/2) { + mapoffs -= pagesize/2; mapsize += pagesize/2; } + while(1) { + fseeko (disk, mapoffs, SEEK_SET); + fread (buffer, 1, mapsize, disk); + ___ unsigned char* p = + buffer + mapsize - sizeof(struct zzip_disk_trailer); + for (; p >= buffer ; p--) + { + zzip_off_t root; /* (struct zzip_disk_entry*) */ + if (zzip_disk_trailer_check_magic(p)) { + root = zzip_disk_trailer_rootseek ( + (struct zzip_disk_trailer*)p); + if (root > disksize - (long)sizeof(struct zzip_disk_trailer)) { + /* first disk_entry is after the disk_trailer? can't be! */ + zzip_off_t rootsize = zzip_disk_trailer_rootsize ( + (struct zzip_disk_trailer*)p); + if (rootsize > mapoffs) continue; + /* a common brokeness that can be fixed: we just assume the + * central directory was written directly before : */ + root = mapoffs - rootsize; + } + } else if (zzip_disk64_trailer_check_magic(p)) { + if (sizeof(zzip_off_t) < 8) return 0; + root = zzip_disk64_trailer_rootseek ( + (struct zzip_disk64_trailer*)p); + } else continue; + + assert (0 <= root && root < mapsize); + fseeko (disk, root, SEEK_SET); + fread (disk_(entry), 1, sizeof(*disk_(entry)), disk); + if (zzip_disk_entry_check_magic(entry)) { + free (buffer); + entry->headseek = root; + entry->diskfile = disk; + entry->disksize = disksize; + if (prescan_entry(entry)) goto nomem; + return entry; + } + } ____; + if (! mapoffs) break; assert (mapsize >= pagesize/2); + mapoffs -= pagesize/2; /* mapsize += pagesize/2; */ + mapsize = pagesize; /* if (mapsize > pagesize) ... */ + if (disksize - mapoffs > 64*1024) break; + } + free (buffer); + nomem: + free (entry); ____;____;____;____;____;____; + return 0; +} + +/** => zzip_entry_findfile + * + * This function takes an existing "entry" in the central root directory + * (e.g. from zzip_entry_findfirst) and moves it to point to the next entry. + * On error it returns 0, otherwise the old entry. If no further match is + * found then null is returned and the entry already free()d. If you want + * to stop searching for matches before that case then please call + * => zzip_entry_free on the cursor struct ZZIP_ENTRY. + */ +zzip__new__ ZZIP_ENTRY* +zzip_entry_findnext(ZZIP_ENTRY* _zzip_restrict entry) +{ + if (! entry) return entry; + if (! zzip_disk_entry_check_magic (entry)) goto err; + ___ zzip_off_t seek = + entry->headseek + zzip_disk_entry_sizeto_end (disk_(entry)); + if (seek + (zzip_off_t) sizeof(*disk_(entry)) > entry->disksize) goto err; + + fseeko (entry->diskfile, seek, SEEK_SET); + fread (disk_(entry), 1, sizeof(*disk_(entry)), entry->diskfile); + entry->headseek = seek; + if (! zzip_disk_entry_check_magic (entry)) goto err; + if (prescan_entry(entry)) goto err; + return entry; + err: + zzip_entry_free (entry); + return 0; ____; +} + +/** => zzip_entry_findfile + * this function releases the malloc()ed areas needed for zzip_entry, the + * pointer is invalid afterwards. This function has #define synonyms of + * zzip_entry_findlast(), zzip_entry_findlastfile(), zzip_entry_findlastmatch() + */ +int +zzip_entry_free(ZZIP_ENTRY* entry) +{ + if (! entry) return 0; + prescan_clear (entry); + free (entry); + return 1; +} + +/** search for files in the (fseeko) zip central directory + * + * This function is given a filename as an additional argument, to find the + * disk_entry matching a given filename. The compare-function is usually + * strcmp or strcasecmp or perhaps strcoll, if null then strcmp is used. + * - use null as argument for "old"-entry when searching the first + * matching entry, otherwise the last returned value if you look for other + * entries with a special "compare" function (if null then a doubled search + * is rather useless with this variant of _findfile). If no further entry is + * found then null is returned and any "old"-entry gets already free()d. + */ +zzip__new__ ZZIP_ENTRY* +zzip_entry_findfile(FILE* disk, char* filename, + ZZIP_ENTRY* _zzip_restrict entry, + zzip_strcmp_fn_t compare) +{ + if (! filename || ! disk) return 0; + entry = ( ! entry ) ? zzip_entry_findfirst (disk) + : zzip_entry_findnext (entry); + if (! compare) compare = (zzip_strcmp_fn_t)(strcmp); + + for (; entry ; entry = zzip_entry_findnext (entry)) + { /* filenames within zip files are often not null-terminated! */ + char* realname = zzip_entry_strdup_name (entry); + if (! realname) continue; + if (! compare (filename, realname)) { + free (realname); return entry; + } else { + free (realname); continue; + } + } + return 0; +} + +#ifdef ZZIP_HAVE_FNMATCH_H +#define _zzip_fnmatch fnmatch +# ifdef FNM_CASEFOLD +# define _zzip_fnmatch_CASEFOLD FNM_CASEFOLD +# else +# define _zzip_fnmatch_CASEFOLD 0 +# endif +#else +# define _zzip_fnmatch_CASEFOLD 0 +/* if your system does not have fnmatch, we fall back to strcmp: */ +static int _zzip_fnmatch(char* pattern, char* string, int flags) +{ + puts ("<zzip:strcmp>"); + return strcmp (pattern, string); +} +#endif + +/** => zzip_entry_findfile + * + * This function uses a compare-function with an additional argument + * and it is called just like fnmatch(3) from POSIX.2 AD:1993), i.e. + * the argument filespec first and the ziplocal filename second with + * the integer-flags put in as third to the indirect call. If the + * platform has fnmatch available then null-compare will use that one + * and otherwise we fall back to mere strcmp, so if you need fnmatch + * searching then please provide an implementation somewhere else. + * - use null as argument for "after"-entry when searching the first + * matching entry, or the last disk_entry return-value to find the + * next entry matching the given filespec. If no further entry is + * found then null is returned and any "old"-entry gets already free()d. + */ +zzip__new__ ZZIP_ENTRY* +zzip_entry_findmatch(FILE* disk, char* filespec, + ZZIP_ENTRY* _zzip_restrict entry, + zzip_fnmatch_fn_t compare, int flags) +{ + if (! filespec || ! disk) return 0; + entry = ( ! entry ) ? zzip_entry_findfirst (disk) + : zzip_entry_findnext (entry); + if (! compare) compare = (zzip_fnmatch_fn_t) _zzip_fnmatch; + + for (; entry ; entry = zzip_entry_findnext (entry)) + { /* filenames within zip files are often not null-terminated! */ + char* realname = zzip_entry_strdup_name (entry); + if (! realname) continue; + if (! compare (filespec, realname, flags)) { + free (realname); return entry; + } else { + free (realname); continue; + } + } + return 0; +} + +/* ====================================================================== */ + +/** + * typedef struct zzip_disk_file ZZIP_ENTRY_FILE; + */ +struct zzip_entry_file /* : zzip_file_header */ +{ + struct zzip_file_header header; /* fopen detected header */ + ZZIP_ENTRY* entry; /* fopen entry */ + zzip_off_t data; /* for stored blocks */ + zzip_size_t avail; /* memorized for checks on EOF */ + zzip_size_t compressed; /* compressed flag and datasize */ + zzip_size_t dataoff; /* offset from data start */ + z_stream zlib; /* for inflated blocks */ + unsigned char buffer[PAGESIZE]; /* work buffer for inflate algorithm */ +}; + +/** open a file within a zip disk for reading + * + * This function does take an "entry" argument and copies it (or just takes + * it over as owner) to a new ZZIP_ENTRY_FILE handle structure. That + * structure contains also a zlib buffer for decoding. This function does + * seek to the file_header of the given "entry" and validates it for the + * data buffer following it. We do also prefetch some data from the data + * buffer thereby trying to match the disk pagesize for faster access later. + * The => zzip_entry_fread will then read in chunks of pagesizes which is + * the size of the internal readahead buffer. If an error occurs then null + * is returned. + */ +zzip__new__ ZZIP_ENTRY_FILE* +zzip_entry_fopen (ZZIP_ENTRY* entry, int takeover) +{ + if (! entry) return 0; + if (! takeover) { + ZZIP_ENTRY* found = malloc (sizeof(*entry)); + if (! found) return 0; + memcpy (found, entry, sizeof(*entry)); /* prescan_copy */ + found->tail = malloc (found->tailalloc); + if (! found->tail) { free (found); return 0; } + memcpy (found->tail, entry->tail, entry->tailalloc); + entry = found; + } + ___ ZZIP_ENTRY_FILE* file = malloc(sizeof(*file)); + if (! file) goto fail1; + file->entry = entry; + if (! zzip_entry_fread_file_header (entry, &file->header)) + goto fail2; + file->avail = zzip_file_header_usize (&file->header); + file->data = zzip_entry_data_offset (entry); + file->dataoff = 0; + + if (! file->avail || zzip_file_header_data_stored (&file->header)) + { file->compressed = 0; return file; } + + file->compressed = zzip_file_header_csize (&file->header); + file->zlib.opaque = 0; + file->zlib.zalloc = Z_NULL; + file->zlib.zfree = Z_NULL; + + ___ zzip_off_t seek = file->data; + seek += sizeof(file->buffer); seek -= seek & (sizeof(file->buffer)-1); + assert (file->data < seek); /* pre-read to next PAGESIZE boundary... */ + fseeko (file->entry->diskfile, file->data + file->dataoff, SEEK_SET); + file->zlib.next_in = file->buffer; + file->zlib.avail_in = fread (file->buffer, 1, seek - file->data, + file->entry->diskfile); + file->dataoff += file->zlib.avail_in; ____; + + if (! zzip_file_header_data_deflated (&file->header) + || inflateInit2 (& file->zlib, -MAX_WBITS) != Z_OK) goto fail2; + + return file; + fail2: + free (file); + fail1: + zzip_entry_free (entry); + return 0; ____; +} + +/** => zzip_entry_fopen + * + * This function opens a file found by name, so it does a search into + * the zip central directory with => zzip_entry_findfile and whatever + * is found first is given to => zzip_entry_fopen + */ +zzip__new__ ZZIP_ENTRY_FILE* +zzip_entry_ffile (FILE* disk, char* filename) +{ + ZZIP_ENTRY* entry = zzip_entry_findfile (disk, filename, 0, 0); + if (! entry) return 0; + return zzip_entry_fopen (entry, 1); +} + + +/** => zzip_entry_fopen + * + * This function reads more bytes into the output buffer specified as + * arguments. The return value is null on eof or error, the stdio-like + * interface can not distinguish between these so you need to check + * with => zzip_entry_feof for the difference. + */ +zzip_size_t +zzip_entry_fread (void* ptr, zzip_size_t sized, zzip_size_t nmemb, + ZZIP_ENTRY_FILE* file) +{ + if (! file) return 0; + ___ zzip_size_t size = sized*nmemb; + if (! file->compressed) { + if (size > file->avail) size = file->avail; + fread (ptr, 1, size, file->entry->diskfile); + file->dataoff += size; + file->avail -= size; + return size; + } + + file->zlib.avail_out = size; + file->zlib.next_out = ptr; + ___ zzip_size_t total_old = file->zlib.total_out; + while (1) { + if (! file->zlib.avail_in) { + size = file->compressed - file->dataoff; + if (size > sizeof(file->buffer)) size = sizeof(file->buffer); + /* fseek (file->data + file->dataoff, file->entry->diskfile); */ + file->zlib.avail_in = fread (file->buffer, 1, size, + file->entry->diskfile); + file->zlib.next_in = file->buffer; + file->dataoff += file->zlib.avail_in; + } + if (! file->zlib.avail_in) return 0; + + ___ int err = inflate (& file->zlib, Z_NO_FLUSH); + if (err == Z_STREAM_END) + file->avail = 0; + else if (err == Z_OK) + file->avail -= file->zlib.total_out - total_old; + else + return 0; + ____; + if (file->zlib.avail_out && ! file->zlib.avail_in) continue; + return file->zlib.total_out - total_old; + }____;____; +} + +/** => zzip_entry_fopen + * This function releases any zlib decoder info needed for decompression + * and dumps the ZZIP_ENTRY_FILE struct then. + */ +int +zzip_entry_fclose (ZZIP_ENTRY_FILE* file) +{ + if (! file) return 0; + if (file->compressed) + inflateEnd (& file->zlib); + zzip_entry_free (file->entry); + free (file); + return 0; +} + +/** => zzip_entry_fopen + * + * This function allows to distinguish an error from an eof condition. + * Actually, if we found an error but we did already reach eof then we + * just keep on saying that it was an eof, so the app can just continue. + */ +int +zzip_entry_feof (ZZIP_ENTRY_FILE* file) +{ + return ! file || ! file->avail; +} |