Init commit

This commit is contained in:
Kuba Podgorski 2015-03-23 15:08:14 -07:00
parent b7b5ff66a9
commit e5f376e4ee
6 changed files with 5399 additions and 3 deletions

View File

@ -1,3 +1,5 @@
A portable, simple *zip* library written in C
-------------------------------------------
This is done by layering functions on top of the [miniz](https://code.google.com/p/miniz) API.n in C
A portable, simple zip library written in C
===========================================
<img src="zip.png" name="zip" />
This is done by layering functions on top of the [miniz](https://code.google.com/p/miniz) v1.15 API.

26
UNLICENSE Normal file
View File

@ -0,0 +1,26 @@
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/

4923
src/miniz.h Normal file

File diff suppressed because it is too large Load Diff

376
src/zip.c Normal file
View File

@ -0,0 +1,376 @@
/*
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include "miniz.h"
#include "zip.h"
#define cleanup(ptr) do { if (ptr) { free((void *)ptr); ptr = NULL; } } while (0)
#define strclone(ptr) ((ptr) ? strdup(ptr) : NULL)
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
/* Win32, OS/2, DOS */
# define HAS_DEVICE(P) ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && (P)[1] == ':')
# define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
#endif
#ifndef FILESYSTEM_PREFIX_LEN
# define FILESYSTEM_PREFIX_LEN(Filename) 0
#endif
#ifndef ISSLASH
# define ISSLASH(C) ((C) == '/')
#endif
static char *basename (const char *name) {
char const *base = name += FILESYSTEM_PREFIX_LEN (name);
int all_slashes = 1;
char const *p;
for (p = name; *p; p++) {
if (ISSLASH (*p))
base = p + 1;
else
all_slashes = 0;
}
/* If NAME is all slashes, arrange to return `/'. */
if (*base == '\0' && ISSLASH (*name) && all_slashes)
--base;
return (char *)base;
}
struct zip_entry_t {
const char *name;
mz_uint64 uncomp_size;
mz_uint64 comp_size;
mz_uint32 uncomp_crc32;
mz_uint64 offset;
mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
mz_uint64 header_offset;
mz_uint16 method;
mz_zip_writer_add_state state;
tdefl_compressor comp;
};
struct zip_t {
mz_zip_archive archive;
mz_uint level;
struct zip_entry_t entry;
};
zip_t *zip_open(const char *zipname, int level, int add) {
if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL
return NULL;
}
if (level < 0) level = MZ_DEFAULT_LEVEL;
if ((level & 0xF) > MZ_UBER_COMPRESSION) {
// Wrong compression level
return NULL;
}
zip_t *zip = (zip_t *)calloc(1, sizeof(zip_t));
if (zip) {
zip->level = level;
if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) {
// Cannot initialize zip_archive writer
cleanup(zip);
}
}
return zip;
}
void zip_close(zip_t *zip) {
if (zip) {
// Always finalize, even if adding failed for some reason, so we have a valid central directory.
mz_zip_writer_finalize_archive(&(zip->archive));
mz_zip_writer_end(&(zip->archive));
cleanup(zip);
}
}
int zip_entry_open(zip_t *zip, const char *entryname) {
if (!zip || !entryname) {
return -1;
}
size_t entrylen = strlen(entryname);
if (entrylen < 1) {
return -1;
}
zip->entry.name = strclone(entryname);
if (!zip->entry.name) {
// Cannot parse zip entry name
return -1;
}
zip->entry.comp_size = 0;
zip->entry.uncomp_size = 0;
zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
zip->entry.offset = zip->archive.m_archive_size;
zip->entry.header_offset = zip->archive.m_archive_size;
memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
zip->entry.method = 0;
mz_zip_archive *pzip = &(zip->archive);
mz_uint num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
// Wrong zip mode
return -1;
}
if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
// Wrong zip compression level
return -1;
}
// no zip64 support yet
if ((pzip->m_total_files == 0xFFFF) || ((pzip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + entrylen) > 0xFFFFFFFF)) {
// No zip64 support yet
return -1;
}
if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset, num_alignment_padding_bytes + sizeof(zip->entry.header))) {
// Cannot memset zip entry header
return -1;
}
zip->entry.header_offset += num_alignment_padding_bytes;
if (pzip->m_file_offset_alignment) { MZ_ASSERT((zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); }
zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header);
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, entrylen) != entrylen) {
// Cannot write data to zip entry
return -1;
}
zip->entry.offset += entrylen;
mz_uint level = zip->level & 0xF;
if (level) {
zip->entry.state.m_pZip = pzip;
zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
zip->entry.state.m_comp_size = 0;
if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, &(zip->entry.state), tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) {
// Cannot initialize the zip compressor
return -1;
}
}
return 0;
}
int zip_entry_close(zip_t *zip) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
mz_zip_archive *pzip = &(zip->archive);
mz_uint level = zip->level & 0xF;
if (level) {
tdefl_status done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
// Cannot flush compressed buffer
cleanup(zip->entry.name);
return -1;
}
zip->entry.comp_size = zip->entry.state.m_comp_size;
zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
zip->entry.method = MZ_DEFLATED;
}
mz_uint16 entrylen = (mz_uint16)strlen(zip->entry.name);
time_t t = time(NULL);
struct tm *tm = localtime(&t);
mz_uint16 dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
mz_uint16 dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
// no zip64 support yet
if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) {
// No zip64 support, yet
cleanup(zip->entry.name);
return -1;
}
if (!mz_zip_writer_create_local_dir_header(pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date)) {
// Cannot create zip entry header
cleanup(zip->entry.name);
return -1;
}
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
// Cannot write zip entry header
cleanup(zip->entry.name);
return -1;
}
if (!mz_zip_writer_add_to_central_dir(pzip, zip->entry.name, entrylen, NULL, 0, "", 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, 0)) {
// Cannot write to zip central dir
cleanup(zip->entry.name);
return -1;
}
pzip->m_total_files++;
pzip->m_archive_size = zip->entry.offset;
cleanup(zip->entry.name);
return 0;
}
int zip_entry_write(zip_t *zip, const void *buf, size_t bufsize) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
mz_zip_archive *pzip = &(zip->archive);
if (buf && bufsize > 0) {
zip->entry.uncomp_size += bufsize;
zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
mz_uint level = zip->level & 0xF;
if (!level) {
if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, bufsize) != bufsize)) {
// Cannot write buffer
return -1;
}
zip->entry.offset += bufsize;
zip->entry.comp_size += bufsize;
} else {
tdefl_status status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, TDEFL_NO_FLUSH);
if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
// Cannot compress buffer
return -1;
}
}
}
return 0;
}
int zip_entry_fwrite(zip_t *zip, const char *filename) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = { 0 };
FILE *stream = fopen(filename, "rb");
if (!stream) {
// Cannot open filename
return -1;
}
int status = 0;
size_t n = 0;
while((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > 0) {
if (zip_entry_write(zip, buf, n) < 0) {
status = -1;
break;
}
}
fclose(stream);
return status;
}
int zip_create(zip_t *zip, const char *filenames[], size_t len) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
for (int i = 0; i < len; ++i) {
const char *name = filenames[i];
if (!name) continue;
if (!mz_zip_writer_add_file(&(zip->archive), basename(name), name, "", 0, zip->level)) {
// Cannot add file to zip_archive
return -1;
}
}
return 0;
}
int zip_extract(const char *zipname, const char *dir, int (* on_extract)(const char *filename, void *arg), void *arg) {
int status = 0;
char path[MAX_PATH + 1] = { 0 };
size_t dirlen = strlen(dir);
if (dirlen + 1 > MAX_PATH) {
return -1;
}
mz_zip_archive zip_archive;
if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
// Cannot memset zip archive
return -1;
}
if (!zipname || !dir) {
// Cannot parse zip archive name
return -1;
}
// Now try to open the archive.
if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
// Cannot initialize zip_archive reader
status = -1;
goto finally;
}
strcpy(path, dir);
if (!ISSLASH(path[dirlen -1])) {
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
path[dirlen] = '\\';
#else
path[dirlen] = '/';
#endif
++dirlen;
}
// Get and print information about each file in the archive.
mz_zip_archive_file_stat info;
int i = 0, n = (int)mz_zip_reader_get_num_files(&zip_archive);
for (; i < n; ++i) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) {
// Cannot get information about zip archive;
status = -1;
break;
}
strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) {
// Cannot extract zip archive to file
status = -1;
break;
}
if (on_extract) {
if (on_extract(path, arg) < 0) {
status = -1;
break;
}
}
}
// Close the archive, freeing any resources it was using
if (!mz_zip_reader_end(&zip_archive)) {
// Cannot end zip reader
status = -1;
}
finally:
return status;
}

69
src/zip.h Normal file
View File

@ -0,0 +1,69 @@
/*
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#ifndef ZIP_H
#define ZIP_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
// This data structure is used throughout the library to represent zip archive - forward declaration.
typedef struct zip_t zip_t;
// Opens zip archive with compression level.
// If add is 0 then new archive will be created,
// otherwise function will try to open existing archive to add data.
// Compression levels: 0-9 are the standard zlib-style levels.
// Returns pointer to zip_t structure or NULL on error.
zip_t *zip_open(const char *zipname, int level, int add);
// Closes zip archive, releases resources - always finalize.
void zip_close(zip_t *zip);
// Opens a new entry for writing in a zip archive.
// Returns 0 or -1 on error.
int zip_entry_open(zip_t *zip, const char *entryname);
// Closes zip entry, flushes buffer and releases resources.
// Returns 0 or -1 on error.
int zip_entry_close(zip_t *zip);
// Compresses an input buffer for the current zip entry.
// Returns 0 or -1 on error.
int zip_entry_write(zip_t *zip, const void *buf, size_t bufsize);
// Compresses a file for the current zip entry.
// Returns 0 or -1 on error.
int zip_entry_fwrite(zip_t *zip, const char *filename);
// Puts len files into a single zip archive
// Returns 0 or -1 on error.
int zip_create(zip_t *zip, const char *filenames[], size_t len);
// Extracts a zip archive file into dir.
// Returns 0 or -1 on error.
int zip_extract(const char *zipname, const char *dir, int (* on_extract)(const char *filename, void *arg), void *arg);
#ifdef __cplusplus
}
#endif
#endif

BIN
zip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB