Need help with zip?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

kuba--
614 Stars 145 Forks The Unlicense 213 Commits 6 Opened issues

Description

A portable, simple zip library written in C

Services available

!
?

Need anything else?

Contributors list

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

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.

Examples

  • Create a new zip archive with default compression level. ```c struct zipt *zip = zipopen("foo.zip", 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.

    c
    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. ```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("foo.zip", "/tmp", onextract_entry, &arg); ```

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

struct zipt *zip = zipopen("foo.zip", 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("foo.zip", 0, 'r'); { zipentryopen(zip, "foo-1.txt"); { bufsize = zipentrysize(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. ```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("foo.zip", 0, 'r'); { zipentryopen(zip, "foo-1.txt"); { zipentryextract(zip, onextract, &buf); } zipentryclose(zip); } zip_close(zip);

free(buf.data); ```

  • Extract a zip entry into a file.

    c
    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 a memory (stream API).
char *buf = NULL;
ssize_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);

  • List of all zip entries

    c
    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) ```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); } ```

  • Deletes zip archive entries. ```c char *entries[] = {"unused.txt", "remove.ini", "delete.me"};

struct zipt *zip = zipopen("foo.zip", 0, 'd'); { zipentriesdelete(zip, entries, 3); } zip_close(zip); ```

Bindings

Compile zip library as a dynamic library.

shell
$ 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/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)

} </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/rust.zip").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);
        }
        zip_entry_close(zip);
    }
    zip_close(zip);
}

}

Ruby (ffi)

Install ffi gem.

shell
$ 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/ruby.zip", 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

shell
$ 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/python.zip", 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 "libzip.so" func zip_open(zipname: string, level: int, mode: char) -> c_ptr
extern "libzip.so" func zip_close(zip: c_ptr) -> void

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

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

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

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

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

zip_close(zip);
0

}

Ring

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

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

Check out more cool projects which use this library:

  • Filament: Filament is a real-time physically based rendering engine for Android, iOS, Linux, macOS, Windows, and WebGL. It is designed to be as small as possible and as efficient as possible on Android.
  • Hermes JS Engine: Hermes is a JavaScript engine optimized for fast start-up of React Native apps on Android. It features ahead-of-time static optimization and compact bytecode.
  • Object-Oriented Graphics Rendering Engine: OGRE is a scene-oriented, flexible 3D engine written in C++ designed to make it easier and more intuitive for developers to produce games and demos utilising 3D hardware.
  • Open Asset Import Library: A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data.
  • PowerToys: Set of utilities for power users to tune and streamline their Windows 10 experience for greater productivity.
  • The Ring Programming Language: Innovative and practical general-purpose multi-paradigm language.
  • The V Programming Language: Simple, fast, safe, compiled. For developing maintainable software.
  • TIC-80: TIC-80 is a FREE and OPEN SOURCE fantasy computer for making, playing and sharing tiny games.
  • Urho3D: Urho3D is a free lightweight, cross-platform 2D and 3D game engine implemented in C++ and released under the MIT license. Greatly inspired by OGRE and Horde3D.
  • Vcpkg: Vcpkg helps you manage C and C++ libraries on Windows, Linux and MacOS.
  • and more...

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.