mirror of
https://github.com/QuasarApp/zip.git
synced 2025-04-26 04:34:31 +00:00
Init commit
This commit is contained in:
parent
b7b5ff66a9
commit
e5f376e4ee
@ -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
26
UNLICENSE
Normal 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
4923
src/miniz.h
Normal file
File diff suppressed because it is too large
Load Diff
376
src/zip.c
Normal file
376
src/zip.c
Normal 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
69
src/zip.h
Normal 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
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user