Zip
A portable, simple zip library written in C
Install / Use
/learn @kuba--/ZipREADME
A portable (OSX/Linux/Windows/Android/iOS), simple zip library written in C
This is done by hacking awesome miniz library and layering functions on top of the miniz v3.1.1 API.
The Idea
<img src="zip.png" name="zip" /> ... Some day, I was looking for zip library written in C for my project, but I could not find anything simple enough and lightweight. Everything what I tried required 'crazy mental gymnastics' to integrate or had some limitations or was too heavy. I hate frameworks, factories and adding new dependencies. If I must to install all those dependencies and link new library, I'm getting almost sick. I wanted something powerful and small enough, so I could add just a few files and compile them into my project. And finally I found miniz. Miniz is a lossless, high performance data compression library in a single source file. I only needed simple interface to append buffers or files to the current zip-entry. Thanks to this feature I'm able to merge many files/buffers and compress them on-the-fly.It was the reason, why I decided to write zip module on top of the miniz. It required a little bit hacking and wrapping some functions, but I kept simplicity. So, you can grab these 3 files and compile them into your project. I hope that interface is also extremely simple, so you will not have any problems to understand it.
Examples
- Create a new zip archive with default compression level.
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
{
zip_entry_open(zip, "foo-1.txt");
{
const char *buf = "Some data here...\0";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
zip_entry_open(zip, "foo-2.txt");
{
// merge 3 files into one entry and compress them on-the-fly.
zip_entry_fwrite(zip, "foo-2.1.txt");
zip_entry_fwrite(zip, "foo-2.2.txt");
zip_entry_fwrite(zip, "foo-2.3.txt");
}
zip_entry_close(zip);
}
zip_close(zip);
- Append to the existing zip archive.
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
{
zip_entry_open(zip, "foo-3.txt");
{
const char *buf = "Append some data here...\0";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
}
zip_close(zip);
- Extract a zip archive into a folder.
int on_extract_entry(const char *filename, void *arg) {
static int i = 0;
int n = *(int *)arg;
printf("Extracted: %s (%d of %d)\n", filename, ++i, n);
return 0;
}
// From "foo.zip" on disk
int arg = 2;
zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
// Or from memory
arg = 2;
zip_stream_extract(zipstream, zipstreamsize, "/tmp", on_extract_entry, &arg);
- Extract a zip entry into memory.
void *buf = NULL;
size_t bufsize;
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_read(zip, &buf, &bufsize);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf);
- Extract a zip entry into memory (no internal allocation).
unsigned char *buf;
size_t bufsize;
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
bufsize = zip_entry_size(zip);
buf = calloc(sizeof(unsigned char), bufsize);
zip_entry_noallocread(zip, (void *)buf, bufsize);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf);
- Extract a zip entry into memory using callback.
struct buffer_t {
char *data;
size_t size;
};
static size_t on_extract(void *arg, unsigned long long offset, const void *data, size_t size) {
struct buffer_t *buf = (struct buffer_t *)arg;
buf->data = realloc(buf->data, buf->size + size + 1);
assert(NULL != buf->data);
memcpy(&(buf->data[buf->size]), data, size);
buf->size += size;
buf->data[buf->size] = 0;
return size;
}
struct buffer_t buf = {0};
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_extract(zip, on_extract, &buf);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf.data);
- Extract a zip entry into a file.
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-2.txt");
{
zip_entry_fread(zip, "foo-2.txt");
}
zip_entry_close(zip);
}
zip_close(zip);
- Create a new zip archive in memory (stream API).
char *outbuf = NULL;
size_t outbufsize = 0;
const char *inbuf = "Append some data here...\0";
struct zip_t *zip = zip_stream_open(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_write(zip, inbuf, strlen(inbuf));
}
zip_entry_close(zip);
/* copy compressed stream into outbuf */
zip_stream_copy(zip, (void **)&outbuf, &outbufsize);
}
zip_stream_close(zip);
free(outbuf);
- Extract a zip entry into memory (stream API).
char *buf = NULL;
size_t bufsize = 0;
struct zip_t *zip = zip_stream_open(zipstream, zipstreamsize, 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_read(zip, (void **)&buf, &bufsize);
}
zip_entry_close(zip);
}
zip_stream_close(zip);
free(buf);
- Extract a partial zip entry
unsigned char buf[16];
size_t bufsize = sizeof(buf);
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
size_t offset = 4;
ssize_t nread = zip_entry_noallocreadwithoffset(zip, offset, bufsize, (void *)buf);
}
zip_entry_close(zip);
}
zip_close(zip);
- List of all zip entries
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
int i, n = zip_entries_total(zip);
for (i = 0; i < n; ++i) {
zip_entry_openbyindex(zip, i);
{
const char *name = zip_entry_name(zip);
int isdir = zip_entry_isdir(zip);
unsigned long long size = zip_entry_size(zip);
unsigned int crc32 = zip_entry_crc32(zip);
}
zip_entry_close(zip);
}
zip_close(zip);
- Compress folder (recursively)
void zip_walk(struct zip_t *zip, const char *path) {
DIR *dir;
struct dirent *entry;
char fullpath[MAX_PATH];
struct stat s;
memset(fullpath, 0, MAX_PATH);
dir = opendir(path);
assert(dir);
while ((entry = readdir(dir))) {
// skip "." and ".."
if (!strcmp(entry->d_name, ".\0") || !strcmp(entry->d_name, "..\0"))
continue;
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
stat(fullpath, &s);
if (S_ISDIR(s.st_mode))
zip_walk(zip, fullpath);
else {
zip_entry_open(zip, fullpath);
zip_entry_fwrite(zip, fullpath);
zip_entry_close(zip);
}
}
closedir(dir);
}
- Delete zip archive entries.
char *entries[] = {"unused.txt", "remove.ini", "delete.me"};
// size_t indices[] = {0, 1, 2};
struct zip_t *zip = zip_open("foo.zip", 0, 'd');
{
zip_entries_delete(zip, entries, 3);
// you can also delete by index, instead of by name
// zip_entries_deletebyindex(zip, indices, 3);
}
zip_close(zip);
- Create a password-protected zip archive (Traditional PKWARE Encryption).
struct zip_t *zip = zip_open_with_password("secret.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w', "password");
{
zip_entry_open(zip, "secret-1.txt");
{
const char *buf = "Classified data...\0";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
}
zip_close(zip);
- Extract a password-protected zip archive.
void *buf = NULL;
size_t bufsize;
struct zip_t *zip = zip_open_with_password("secret.zip", 0, 'r', "password");
{
zip_entry_open(zip, "secret-1.txt");
{
zip_entry_read(zip, &buf, &bufsize);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf);
- Password-protected archive in memory (stream API).
char *outbuf = NULL;
size_t outbufsize = 0;
struct zip_t *zip = zip_stream_open_with_password(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w', "password");
{
zip_entry_open(zip, "secret-1.txt");
{
const char *buf = "Classified data...\0";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
zip_stream_copy(zip, (void **)&outbuf, &outbufsize);
}
zip_stream_close(zip);
/* read it back */
void *readbuf = NULL;
size_t readsize = 0;
zip = zip_stream_open_with_password(outbuf, outbufsize, 0, 'r', "password");
{
zip_entry_open(zip, "secret-1.txt");
{
zip_entry_read(zip, &readbuf, &readsize);
}
zip_entry_close(zip);
}
zip_stream_close(zip);
free(readbuf);
free(outbuf);
- Delete entries from a password-protected archive.
char *entries[] = {"obsolete.txt", "remove-me.dat"};
struct zip_t *zip = zip_open_with_password("secret.zip", 0, 'd', "password");
{
zip_entries_delete(zip, entries, 2);
}
zip_close(zip);
Bindings
Compile zip library as a dynamic library.
$ mkdir build
$ cd build
$ cmake -DBUILD_SHARED_LIBS=true ..
$ cmake --build .
Go (cgo)
Third party binding: kuba--/c-go-zip
package main
/*
#cgo CFLAGS: -I../src
#cgo LDFLAGS: -L. -lzip
#include <zip.h>
*/
import "C"
import "unsafe"
func main() {
path := C.CString("/tmp/go.zip")
zip := C.zip_open(path, 6, 'w')
entryname := C.CString("test")
C.zip_entry_open(zip, entryname)
content := "test content"
buf := unsafe.Pointer(C.CString(content))
bufsize := C.size_t(len(content))
C.zip_entry_write(zip, buf, bufsize)
C.zip_entry_close(zip)
C.zip_close(zip)
}
Rust (ffi)
extern crate libc;
use std::ffi::CString;
#[repr(C)]
pub struct Zip {
_privat
Related Skills
node-connect
343.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
90.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
