From cb35969fce3800610db21c0385e316a20d6cc188 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 3 Mar 2017 08:44:04 +0800 Subject: [PATCH] junzip: import from DomTerm 912add15f3d0aec From https://github.com/PerBothner/DomTerm.git --- lib/junzip.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/junzip.h | 68 ++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 lib/junzip.c create mode 100644 lib/junzip.h diff --git a/lib/junzip.c b/lib/junzip.c new file mode 100644 index 0000000..d50489f --- /dev/null +++ b/lib/junzip.c @@ -0,0 +1,223 @@ +// Unzip library by Per Bothner and Joonas Pihlajamaa. +// See junzip.h for license and details. + +#include +#include + +#include + +#include "junzip.h" + +#define ZIP_CENTRAL_SIGNATURE 0 +#define ZIP_CENTRAL_VERSION_MADE_BY 4 +#define ZIP_CENTRAL_VERSION_NEEDED_TO_EXTRACT 6 +#define ZIP_CENTRAL_GENERAL_PURPOSE_BIT_FLAG 8 +#define ZIP_CENTRAL_COMPRESSION_METHOD 10 +#define ZIP_CENTRAL_LAST_MOD_FILE_TIME 12 +#define ZIP_CENTRAL_LAST_MOD_FILE_DATE 14 +#define ZIP_CENTRAL_CRC32 16 +#define ZIP_CENTRAL_COMPRESSED_SIZE 20 +#define ZIP_CENTRAL_UNCOMPRESSED_SIZE 24 +#define ZIP_CENTRAL_FILE_NAME_LENGTH 28 +#define ZIP_CENTRAL_EXTRA_FIELD_LENGTH 30 +#define ZIP_CENTRAL_FILE_COMMENT_LENGTH 32 +#define ZIP_CENTRAL_DISK_NUMBER_START 34 +#define ZIP_CENTRAL_INTERNAL_FILE_ATTRIBUTES 36 +#define ZIP_CENTRAL_EXTERNAL_FILE_ATTRIBUTES 38 +#define ZIP_CENTRAL_RELATIVE_OFFSET_OF_LOCAL_HEADER 42 +#define ZIP_CENTRAL_DIRECTORY_LENGTH 46 + +#define ZIP_END_SIGNATURE_OFFSET 0 +#define ZIP_END_DESK_NUMBER 4 +#define ZIP_END_CENTRAL_DIRECTORY_DISK_NUMBER 6 +#define ZIP_END_NUM_ENTRIES_THIS_DISK 8 +#define ZIP_END_NUM_ENTRIES 10 +#define ZIP_END_CENTRAL_DIRECTORY_SIZE 12 +#define ZIP_END_CENTRAL_DIRECTORY_OFFSET 16 +#define ZIP_END_ZIP_COMMENT_LENGTH 20 +#define ZIP_END_DIRECTORY_LENGTH 22 + +#define get_u16(PTR) ((PTR)[0] | ((PTR)[1] << 8)) +#define get_u32(PTR) ((PTR)[0] | ((PTR)[1]<<8) | ((PTR)[2]<<16) | ((PTR)[3]<<24)) + +static int +zf_seek_set(JZFile *zfile, size_t offset) +{ + int new_position = offset; + if (new_position < 0 || new_position > zfile->length) + return -1; + zfile->position = new_position; + return 0; +} + +static int +zf_seek_cur(JZFile *zfile, size_t offset) +{ + int new_position = zfile->position + offset; + if (new_position < 0 || new_position > zfile->length) + return -1; + zfile->position = new_position; + return 0; +} + +static int +zf_seek_end(JZFile *zfile, size_t offset) +{ + int new_position = zfile->length + offset; + if (new_position < 0 || new_position > zfile->length) + return -1; + zfile->position = new_position; + return 0; +} + +size_t zf_read(JZFile *zfile, void *buf, size_t size) +{ + size_t avail = zfile->length - zfile->position; + if (size > avail) + size = avail; + memcpy(buf, zfile->start + zfile->position, size); + zfile->position += size; + return size; +} + +// Read ZIP file end record. Will move within file. +int jzReadEndRecord(JZFile *zip) { + long fileSize, readBytes, i; + + if(zf_seek_end(zip, -ZIP_END_DIRECTORY_LENGTH)) { + return Z_ERRNO; + } + + unsigned char *ptr = zf_current(zip); + while (ptr[0] != 0x50 || ptr[1] != 0x4B || ptr[2] != 0x05 || ptr[3] != 0x06) { + if (ptr == zip->start) { + return Z_ERRNO; + } + ptr--; + } + zip->numEntries = get_u16(ptr + ZIP_END_NUM_ENTRIES); + zip->centralDirectoryOffset= get_u32(ptr + ZIP_END_CENTRAL_DIRECTORY_OFFSET); + if (get_u16(ptr + ZIP_END_DESK_NUMBER) + || get_u16(ptr + ZIP_END_CENTRAL_DIRECTORY_DISK_NUMBER) + || zip->numEntries != get_u16(ptr + ZIP_END_NUM_ENTRIES_THIS_DISK)) { + return Z_ERRNO; + } + + return Z_OK; +} + +// Read ZIP file global directory. Will move within file. +int jzReadCentralDirectory(JZFile *zip, JZRecordCallback callback) { + JZFileHeader header; + int i; + + if(zf_seek_set(zip, zip->centralDirectoryOffset)) { + return Z_ERRNO; + } + + for(i=0; i < zip->numEntries; i++) { + unsigned char *ptr = zf_current(zip); + if (zf_available(zip) < ZIP_CENTRAL_DIRECTORY_LENGTH) { + return Z_ERRNO; + } + zf_seek_cur(zip, ZIP_CENTRAL_DIRECTORY_LENGTH); + if (get_u32(ptr + ZIP_CENTRAL_SIGNATURE) != 0x02014B50) { + return Z_ERRNO; + } + // Construct JZFileHeader from global file header + header.compressionMethod = get_u16(ptr + ZIP_CENTRAL_COMPRESSION_METHOD); + header.crc32 = get_u32(ptr + ZIP_CENTRAL_CRC32); + header.compressedSize = get_u32(ptr + ZIP_CENTRAL_COMPRESSED_SIZE); + header.uncompressedSize = get_u32(ptr + ZIP_CENTRAL_UNCOMPRESSED_SIZE); + header.fileNameLength = get_u16(ptr + ZIP_CENTRAL_FILE_NAME_LENGTH); + header.extraFieldLength = get_u16(ptr + ZIP_CENTRAL_EXTRA_FIELD_LENGTH); + header.offset = get_u32(ptr + ZIP_CENTRAL_RELATIVE_OFFSET_OF_LOCAL_HEADER); + + header.fileNameStart = zf_tell(zip); + if (zf_seek_cur(zip, header.fileNameLength + header.extraFieldLength + get_u16(ptr + ZIP_CENTRAL_FILE_COMMENT_LENGTH))) { + return Z_ERRNO; + } + + if(!callback(zip, i, &header)) + break; // end if callback returns zero + } + + return Z_OK; +} + +int jzSeekData(JZFile *zip, JZFileHeader *entry) { + size_t offset = entry->offset; + offset += ZIP_LOCAL_FILE_HEADER_LENGTH; + offset += entry->fileNameLength + entry->extraFieldLength; + if (offset < 0 || offset > zip->length) + return Z_STREAM_END; + zip->position = offset; + return Z_OK; +} + +// Read data from file stream, described by header, to preallocated buffer +int jzReadData(JZFile *zip, JZFileHeader *header, void *buffer) { + unsigned char *bytes = (unsigned char *)buffer; // cast + long compressedLeft, uncompressedLeft; + z_stream strm; + int ret; + + if(header->compressionMethod == 0) { // Store - just read it + if (zf_read(zip, buffer, header->uncompressedSize) < + header->uncompressedSize) + return Z_ERRNO; + } else if (header->compressionMethod == 8) { // Deflate - using zlib + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + strm.avail_in = 0; + strm.next_in = Z_NULL; + + // Use inflateInit2 with negative window bits to indicate raw data + if ((ret = inflateInit2(&strm, -MAX_WBITS)) != Z_OK) + return ret; // Zlib errors are negative + + // Inflate compressed data + for (compressedLeft = header->compressedSize, + uncompressedLeft = header->uncompressedSize; + compressedLeft && uncompressedLeft && ret != Z_STREAM_END; + compressedLeft -= strm.avail_in) { + // Read next chunk + unsigned char *ptr = zf_current(zip); + strm.avail_in = compressedLeft; + zf_seek_cur(zip, compressedLeft); + if(strm.avail_in == 0) { + inflateEnd(&strm); + return Z_ERRNO; + } + + strm.next_in = ptr; + strm.avail_out = uncompressedLeft; + strm.next_out = bytes; + + compressedLeft -= strm.avail_in; // inflate will change avail_in + + ret = inflate(&strm, Z_NO_FLUSH); + + if(ret == Z_STREAM_ERROR) return ret; // shouldn't happen + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return ret; + } + + bytes += uncompressedLeft - strm.avail_out; // bytes uncompressed + uncompressedLeft = strm.avail_out; + } + + inflateEnd(&strm); + } else { + return Z_ERRNO; + } + + return Z_OK; +} diff --git a/lib/junzip.h b/lib/junzip.h new file mode 100644 index 0000000..145a70d --- /dev/null +++ b/lib/junzip.h @@ -0,0 +1,68 @@ +/** + * Unzip library by Per Bothner. + * Loosely based on Joonas Pihlajamaa's JUnzip. + * Released into public domain. https://github.com/jokkebk/JUnzip + */ + +#ifndef __JUNZIP_H +#define __JUNZIP_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +// If you don't have stdint.h, the following two lines should work for most 32/64 bit systems +// typedef unsigned int uint32_t; +// typedef unsigned short uint16_t; + +typedef struct JZFile JZFile; + +struct JZFile { + unsigned char *start; + off_t length; + long position; + int numEntries; + uint32_t centralDirectoryOffset; +}; + +#define zf_tell(ZF) ((ZF)->position) +#define zf_available(ZF) ((ZF)->length - (ZF)->position) +#define zf_current(ZF) ((ZF)->start + (ZF)->position) + +#define ZIP_LOCAL_FILE_HEADER_LENGTH 30 + +typedef struct { + uint16_t compressionMethod; + uint32_t crc32; + uint32_t compressedSize; + uint32_t uncompressedSize; + long fileNameStart; + uint16_t fileNameLength; + uint16_t extraFieldLength; // unsupported + uint32_t offset; +} JZFileHeader; + +// Callback prototype for central and local file record reading functions +typedef int (*JZRecordCallback)(JZFile *zip, int index, JZFileHeader *header); + +// Read ZIP file end record. Will move within file. +int jzReadEndRecord(JZFile *zip); + +// Read ZIP file global directory. Will move within file. +// Callback is called for each record, until callback returns zero +int jzReadCentralDirectory(JZFile *zip, JZRecordCallback callback); + + // See to the start of the actual data of the given entry. +int jzSeekData(JZFile *zip, JZFileHeader *header); + +// Read data from file stream, described by header, to preallocated buffer +// Return value is zlib coded, e.g. Z_OK, or error code +int jzReadData(JZFile *zip, JZFileHeader *header, void *buffer); + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif -- 2.7.4