kuba--/zip

repository·master·Indexed 23 days ago

https://github.com/kuba--/zip

A portable, lightweight C library for ZIP compression and extraction built on top of miniz. It supports disk-based and stream-based operations, Traditional PKWARE Encryption, and ZIP64. The library provides APIs for creating, appending to, and extracting archives, as well as deleting entries and listing archive contents. It includes two command-line tools: zipcli for creating and managing archives, and unzipcli for extracting or listing contents.

Tokens
3.4K
Snippets
10
Records
14
Agent score
82%

What's inside zip

  1. Disable ZIP64 output

    master

    By default, opening an archive for writing with the literal mode character 'w' enables ZIP64 output. To produce a ZIP archive that is NOT ZIP64, use an alternate mode value that selects the same semantic mode but does not equal the literal 'w'.

    Use the following pattern (subtracting 64 from the character literal) to select the non-ZIP64 variant:

    • Write mode: 'w' - 64 (integer value 55)
    • Read mode: 'r' - 64
    • Append mode: 'a' - 64
    • Delete mode: 'd' - 64
  2. Configure compile-time features

    master

    The library supports several flags to reduce binary size by stripping unused functionality. All features are enabled by default.

    FlagDefaultEffect when disabled (=0)
    ZIP_ENABLE_DEFLATE1Removes compression/writing APIs (zip_entry_write, zip_entry_fwrite, 'w', 'a', 'd' modes return error).
    ZIP_ENABLE_INFLATE1Removes decompression/extraction APIs (zip_entry_read, zip_entry_fread, zip_entry_extract, zip_extract, 'r' mode returns error).
    ZIP_HAVE_SYMLINK1 (Unix/macOS), 0 (Windows)Symlink entries are extracted as regular file copies instead of actual symlinks.
    MINIZ_NO_STDIO0Disables usage of stdio in miniz and zip.c

    Using CMake:

    cmake .. -DZIP_ENABLE_DEFLATE=OFF
    cmake .. -DZIP_ENABLE_INFLATE=OFF
    cmake .. -DZIP_HAVE_SYMLINK=OFF

    Using Compiler Flags:

    cc -DZIP_ENABLE_DEFLATE=0 -c zip.c

    Or via header definition:

    #define ZIP_ENABLE_DEFLATE 0
    #include "zip.h"
  3. Use password-protected archives

    master

    The library supports Traditional PKWARE Encryption.

    • To create: Use zip_open_with_password with the 'w' mode and provide the password string.
    • To extract: Use zip_open_with_password with the 'r' mode and provide the password string.
    • To delete: Use zip_open_with_password with the 'd' mode and provide the password string.
  4. List all entries in a zip archive

    master

    To iterate through all entries in an archive, use zip_entries_total to get the count, then loop through indices using zip_entry_openbyindex. For each entry, you can retrieve metadata such as the name, directory status, symlink status, size, and CRC32.

    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);
            int issymlink = zip_entry_issymlink(zip);
            unsigned long long size = zip_entry_size(zip);
            unsigned int crc32 = zip_entry_crc32(zip);
        }
        zip_entry_close(zip);
    }
    zip_close(zip);
  5. Append to an existing zip archive

    master

    To add new entries to an existing archive, use zip_open with the 'a' mode. The workflow for opening and writing entries remains the same as creating a new 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);
  6. Extract a zip archive to a folder

    master

    Use zip_extract to extract all contents of a zip file from disk into a target directory. This function requires a callback function that is invoked for each extracted entry.

    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);
  7. Extract a zip entry into memory

    master

    There are several ways to extract a single entry into memory:

    1. Internal Allocation: Use zip_entry_read to let the library allocate the buffer for you. You must free() the returned pointer.
    2. No Internal Allocation: Use zip_entry_size to get the size, allocate your own buffer (e.g., via calloc), and then use zip_entry_noallocread to fill it.
    3. Callback-based: Use zip_entry_extract with a custom callback to handle data chunks as they are read, which is useful for dynamic resizing or custom processing.
    // Method 1: Internal Allocation
    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);
    
    // Method 2: 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);
  8. Extract a partial zip entry with offset

    master

    To read a specific segment of an entry, use zip_entry_noallocreadwithoffset. This function reads up to size bytes starting at offset into a caller-owned buffer. It does not perform internal allocations. The call returns the number of bytes actually written, or a negative error code if the offset is invalid.

    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);
            if (nread < 0) {
                // offset out of range or read error
            }
        }
    
        zip_entry_close(zip);
    }
    zip_close(zip);
  9. Delete entries from a zip archive

    master

    You can delete entries from an archive using zip_entries_delete (by providing an array of names) or zip_entries_deletebyindex (by providing an array of indices). The archive must be opened in delete mode ('d').

    char *entries[] = {"unused.txt", "remove.ini", "delete.me"};
    
    struct zip_t *zip = zip_open("foo.zip", 0, 'd');
    {
        zip_entries_delete(zip, entries, 3);
        // or: zip_entries_deletebyindex(zip, indices, 3);
    }
    zip_close(zip);
  10. Create a new zip archive

    master

    To create a new zip archive, use zip_open with the 'w' mode. You then open individual entries within the archive using zip_entry_open, write data to them using zip_entry_write (for buffers) or zip_entry_fwrite (for files), and close the entry with zip_entry_close. Finally, close the archive with zip_close.

    Note that zip_entry_fwrite allows you to merge multiple files into a single entry by calling it multiple times before calling zip_entry_close.

    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);
  11. zipcli CLI option reference

    master

    The following options are available for the zipcli command-line tool:

    OptionArgumentDescription
    -oFILEOutput archive filename (default: out.zip)
    -pPASSEncrypt with password (Traditional PKWARE Encryption)
    -lLEVELCompression level 0-9 (default: 6)
    -a(none)Append to existing archive instead of creating new
    -h(none)Show help
    --(none)Stop processing options

    Error Conditions:

    • If -o, -p, or -l are provided without their required arguments, the tool returns an error.
    • If the compression level is not within the 0-9 range, the tool returns an error.
    • If no input files are provided, the tool returns an error.
    Options:
      -o FILE    output archive (default: out.zip)
      -p PASS    encrypt with password (Traditional PKWARE Encryption)
      -l LEVEL   compression level 0-9 (default: 6)
      -a         append to existing archive instead of creating new
      -h         show help
      --         stop processing options
  12. unzipcli command options reference

    master

    The following options are available for the unzipcli tool:

    OptionDescription
    -d DIRExtract files into DIR. Defaults to the current directory (.).
    -p PASSDecrypt the archive using the provided PASS word.
    -lList entries in the archive instead of extracting them.
    -oOverwrite existing files without prompting.
    -hShow the help message.
    --Stop processing options; everything following this is treated as a positional argument (archive or entry).
    Options:
      -d DIR     extract into DIR (default: current directory)
      -p PASS    decrypt with password
      -l         list entries instead of extracting
      -o         overwrite existing files without prompting
      -h         show help
      --         stop processing options