by kuba--

kuba-- / zip

A portable, simple zip library written in C

514 Stars 125 Forks Last release: 6 months ago (v0.1.19) The Unlicense 174 Commits 20 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

A portable (OSX/Linux/Windows), simple zip library written in C

This is done by hacking awesome miniz library and layering functions on top of the miniz v1.15 API.

Build Version

The Idea

... 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 powerfull 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.


  • Create a new zip archive with default compression level. ```c struct zipt *zip = zipopen("", ZIPDEFAULTCOMPRESSIONLEVEL, 'w'); { zipentryopen(zip, "foo-1.txt"); { const char *buf = "Some data here...\0"; zipentrywrite(zip, buf, strlen(buf)); } zipentry_close(zip);

    zipentryopen(zip, "foo-2.txt"); { // merge 3 files into one entry and compress them on-the-fly. zipentryfwrite(zip, "foo-2.1.txt"); zipentryfwrite(zip, "foo-2.2.txt"); zipentryfwrite(zip, "foo-2.3.txt"); } zipentryclose(zip); } zip_close(zip); ```

  • Append to the existing zip archive.

    struct zip_t *zip = zip_open("", 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));
  • Extract a zip archive into a folder. ```c int onextractentry(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; }

int arg = 2; zipextract("", "/tmp", onextract_entry, &arg); ```

  • Extract a zip entry into memory. ```c void *buf = NULL; size_t bufsize;

struct zipt *zip = zipopen("", 0, 'r'); { zipentryopen(zip, "foo-1.txt"); { zipentryread(zip, &buf, &bufsize); } zipentryclose(zip); } zip_close(zip);

free(buf); ```

  • Extract a zip entry into memory (no internal allocation). ```c unsigned char *buf; size_t bufsize;

struct zipt *zip = zipopen("", 0, 'r'); { zipentryopen(zip, "foo-1.txt"); { bufsize = zipentrysize(zip); buf = calloc(sizeof(unsigned char), bufsize);

    zip_entry_noallocread(zip, (void *)buf, bufsize);

} zip_close(zip);

free(buf); ```

  • Extract a zip entry into memory using callback. ```c struct buffert { char *data; sizet size; };

static sizet onextract(void *arg, unsigned long long offset, const void *data, sizet size) { struct buffert *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 buffert buf = {0}; struct zipt *zip = zipopen("", 0, 'r'); { zipentryopen(zip, "foo-1.txt"); { zipentryextract(zip, onextract, &buf); } zipentryclose(zip); } zip_close(zip);

free(; ```

  • Extract a zip entry into a file.

    struct zip_t *zip = zip_open("", 0, 'r');
    zip_entry_open(zip, "foo-2.txt");
        zip_entry_fread(zip, "foo-2.txt");
  • List of all zip entries

    struct zip_t *zip = zip_open("", 0, 'r');
    int i, n = zip_total_entries(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);
  • Compress folder (recursively) ```c void zipwalk(struct zipt *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->dname, ".\0") || !strcmp(entry->dname, "..\0")) continue;

    snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->dname); stat(fullpath, &s); if (SISDIR(s.stmode)) zipwalk(zip, fullpath); else { zipentryopen(zip, fullpath); zipentryfwrite(zip, fullpath); zipentryclose(zip); } }

    closedir(dir); } ```


Compile zip library as a dynamic library.

$ mkdir build
$ cd build
$ cmake -DBUILD_SHARED_LIBS=true ..
$ make

Go (cgo)

package main

/* #cgo CFLAGS: -I../src #cgo LDFLAGS: -L. -lzip #include <zip.h> */ import "C" import "unsafe"

func main() { path := C.CString("/tmp/") 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)



} </zip.h>

Rust (ffi)

extern crate libc;
use std::ffi::CString;

#[repr(C)] pub struct Zip { _private: [u8; 0], }

#[link(name = "zip")] extern "C" { fn zip_open(path: *const libc::c_char, level: libc::c_int, mode: libc::c_char) -> *mut Zip; fn zip_close(zip: *mut Zip) -> libc::c_void;

fn zip_entry_open(zip: *mut Zip, entryname: *const libc::c_char) -&gt; libc::c_int;
fn zip_entry_close(zip: *mut Zip) -&gt; libc::c_int;
fn zip_entry_write(
    zip: *mut Zip,
    buf: *const libc::c_void,
    bufsize: libc::size_t,
) -&gt; libc::c_int;


fn main() { let path = CString::new("/tmp/").unwrap(); let mode: libc::c_char = 'w' as libc::c_char;

let entryname = CString::new("test.txt").unwrap();
let content = "test content\0";

unsafe {
    let zip: *mut Zip = zip_open(path.as_ptr(), 5, mode);
        zip_entry_open(zip, entryname.as_ptr());
            let buf = content.as_ptr() as *const libc::c_void;
            let bufsize = content.len() as libc::size_t;
            zip_entry_write(zip, buf, bufsize);


Ruby (ffi)

Install ffi gem.

$ gem install ffi

Bind in your module. ```ruby require 'ffi'

module Zip extend FFI::Library ffi_lib "./libzip.#{::FFI::Platform::LIBSUFFIX}"

attachfunction :zipopen, [:string, :int, :char], :pointer attachfunction :zipclose, [:pointer], :void

attachfunction :zipentryopen, [:pointer, :string], :int attachfunction :zipentryclose, [:pointer], :void attachfunction :zipentry_write, [:pointer, :string, :int], :int end

ptr = Zip.zip_open("/tmp/", 6, "w".bytes()[0])

status = Zip.zipentryopen(ptr, "test")

content = "test content" status = Zip.zipentrywrite(ptr, content, content.size())

Zip.zipentryclose(ptr) Zip.zip_close(ptr) ```

Python (cffi)

Install cffi package

$ pip install cffi

Bind in your package. ```python import ctypes.util from cffi import FFI

ffi = FFI() ffi.cdef(""" struct zipt *zipopen(const char *zipname, int level, char mode); void zipclose(struct zipt *zip);

int zip_entry_open(struct zip_t *zip, const char *entryname);
int zip_entry_close(struct zip_t *zip);
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);


Zip = ffi.dlopen(ctypes.util.find_library("zip"))

ptr = Zip.zip_open("/tmp/", 6, 'w')

status = Zip.zipentryopen(ptr, "test")

content = "test content" status = Zip.zipentrywrite(ptr, content, len(content))

Zip.zipentryclose(ptr) Zip.zip_close(ptr) ```

Never (ffi)

extern "" func zip_open(zipname: string, level: int, mode: char) -> c_ptr
extern "" func zip_close(zip: c_ptr) -> void

extern "" func zip_entry_open(zip: c_ptr, entryname: string) -> int extern "" func zip_entry_close(zip: c_ptr) -> int extern "" func zip_entry_write(zip: c_ptr, buf: string, bufsize: int) -> int extern "" func zip_entry_fwrite(zip: c_ptr, filename: string) -> int

func main() -> int { let content = "Test content"

let zip = zip_open("/tmp/", 6, 'w');

zip_entry_open(zip, "test.file");
zip_entry_fwrite(zip, "/tmp/test.txt");

zip_entry_open(zip, "test.content");
zip_entry_write(zip, content, length(content));




The language comes with RingZip based on this library ```ring load "ziplib.ring"

new Zip { setFileName("") open("w") newEntry() { open("test.c") writefile("test.c") close() } close() } ```

Contribution Rules/Coding Standards

No need to throw away your coding style, just do your best to follow default clang-format style. Apply

to the source files before commit:
for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#')
    clang-format -i $file

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.