6 ex__zip_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_zip.c

Structures

Name
struct MEMBER
one stored member

Functions

Name
int write_zip(const char * path, const MEMBER * m, int count)
write an archive of two members, the way every zip tool lays it out: local headers and data, the central directory, its end record
void put32(unsigned char * p, unsigned long v)
put a little endian 32 bit value
void put16(unsigned char * p, unsigned v)
put a little endian 16 bit value
int process_args(int argc, char ** argv)
read -V
int main(int argc, char ** argv)
run the checks
int file_is(const char * path, const unsigned char * data, size_t len)
is a file these bytes
void expect_true(const char * what, int ok)
count a failed check

Attributes

Name
int log_level
log level from -V
int failures
failed checks

Functions Documentation

function write_zip

static int write_zip(
    const char * path,
    const MEMBER * m,
    int count
)

write an archive of two members, the way every zip tool lays it out: local headers and data, the central directory, its end record

Parameters:

  • path where
  • m the members
  • count how many

Return: 0, or -1

function put32

static void put32(
    unsigned char * p,
    unsigned long v
)

put a little endian 32 bit value

Parameters:

  • p where
  • v the value

function put16

static void put16(
    unsigned char * p,
    unsigned v
)

put a little endian 16 bit value

Parameters:

  • p where
  • v the value

function process_args

static int process_args(
    int argc,
    char ** argv
)

read -V

Parameters:

  • argc argument count
  • argv arguments

Return: 0, or -1 on a bad argument

function main

int main(
    int argc,
    char ** argv
)

run the checks

Parameters:

  • argc argument count
  • argv arguments

Return: 0 on success

function file_is

static int file_is(
    const char * path,
    const unsigned char * data,
    size_t len
)

is a file these bytes

Parameters:

  • path the file
  • data the bytes
  • len how many

Return: non-zero when it is

function expect_true

static void expect_true(
    const char * what,
    int ok
)

count a failed check

Parameters:

  • what the check
  • ok its result

Attributes Documentation

variable log_level

static int log_level = 3;

log level from -V

variable failures

static int failures = 0;

failed checks

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <zlib.h>

#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_zip.h"

static int log_level = LOG_ERR;
static int failures = 0;

static void expect_true(const char* what, int ok) {
    if (!ok) {
        n_log(LOG_ERR, "FAIL: %s", what);
        failures++;
    } else {
        n_log(LOG_INFO, "ok: %s", what);
    }
}

static int process_args(int argc, char** argv) {
    int opt = 0;

    while ((opt = getopt(argc, argv, "V:")) != -1) {
        if (opt == 'V') {
            log_level = atoi(optarg);
        } else {
            return -1;
        }
    }
    return 0;
}

static void put16(unsigned char* p, unsigned v) {
    p[0] = (unsigned char)(v & 0xff);
    p[1] = (unsigned char)((v >> 8) & 0xff);
}

static void put32(unsigned char* p, unsigned long v) {
    put16(p, (unsigned)(v & 0xffff));
    put16(p + 2, (unsigned)((v >> 16) & 0xffff));
}

typedef struct {
    const char* name;
    unsigned method;
    const unsigned char* data;
    size_t len;
} MEMBER;

static int write_zip(const char* path, const MEMBER* m, int count) {
    FILE* f = fopen(path, "wb");
    unsigned long offsets[4], sizes[4], crcs[4];
    unsigned char* packed[4];
    unsigned long dir_at = 0, dir_size = 0, at = 0;
    int i = 0;

    if (!f || count > 4) return -1;
    for (i = 0; i < count; i++) {
        unsigned char header[30];
        size_t n = strlen(m[i].name);

        crcs[i] = crc32(crc32(0L, Z_NULL, 0), m[i].data, (uInt)m[i].len);
        packed[i] = (unsigned char*)malloc(m[i].len + 1024);
        if (m[i].method == 8) {
            z_stream zs;

            memset(&zs, 0, sizeof(zs));
            deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
            zs.next_in = (Bytef*)m[i].data;
            zs.avail_in = (uInt)m[i].len;
            zs.next_out = packed[i];
            zs.avail_out = (uInt)(m[i].len + 1024);
            deflate(&zs, Z_FINISH);
            sizes[i] = zs.total_out;
            deflateEnd(&zs);
        } else {
            memcpy(packed[i], m[i].data, m[i].len);
            sizes[i] = (unsigned long)m[i].len;
        }
        memset(header, 0, sizeof(header));
        put32(header, 0x04034b50ul);
        put16(header + 4, 20);
        put16(header + 8, m[i].method);
        put32(header + 14, crcs[i]);
        put32(header + 18, sizes[i]);
        put32(header + 22, (unsigned long)m[i].len);
        put16(header + 26, (unsigned)n);
        offsets[i] = at;
        fwrite(header, 1, 30, f);
        fwrite(m[i].name, 1, n, f);
        fwrite(packed[i], 1, sizes[i], f);
        at += 30 + (unsigned long)n + sizes[i];
    }
    dir_at = at;
    for (i = 0; i < count; i++) {
        unsigned char record[46];
        size_t n = strlen(m[i].name);

        memset(record, 0, sizeof(record));
        put32(record, 0x02014b50ul);
        put16(record + 4, 20);
        put16(record + 6, 20);
        put16(record + 10, m[i].method);
        put32(record + 16, crcs[i]);
        put32(record + 20, sizes[i]);
        put32(record + 24, (unsigned long)m[i].len);
        put16(record + 28, (unsigned)n);
        put32(record + 42, offsets[i]);
        fwrite(record, 1, 46, f);
        fwrite(m[i].name, 1, n, f);
        dir_size += 46 + (unsigned long)n;
        free(packed[i]);
    }
    {
        unsigned char end[22];

        memset(end, 0, sizeof(end));
        put32(end, 0x06054b50ul);
        put16(end + 8, (unsigned)count);
        put16(end + 10, (unsigned)count);
        put32(end + 12, dir_size);
        put32(end + 16, dir_at);
        fwrite(end, 1, 22, f);
    }
    fclose(f);
    return 0;
}

static int file_is(const char* path, const unsigned char* data, size_t len) {
    FILE* f = fopen(path, "rb");
    unsigned char* got = NULL;
    size_t n = 0;
    int same = 0;

    if (!f) return 0;
    got = (unsigned char*)malloc(len + 1);
    if (got) {
        n = fread(got, 1, len + 1, f);
        same = n == len && memcmp(got, data, len) == 0;
    }
    free(got);
    fclose(f);
    return same;
}

int main(int argc, char** argv) {
    static const char* archive = "ex_zip.tmp.zip";
    static const char* out = "ex_zip.tmp.out";
    static const unsigned char small[] = "a stored member, as it is";
    size_t big_len = 700000, i = 0;
    unsigned char* big = NULL;
    MEMBER members[2];
    N_ZIP_MEMBER info;

    set_log_level(LOG_ERR);
    if (process_args(argc, argv) != 0) {
        fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
        return 2;
    }
    set_log_level(log_level);
    big = (unsigned char*)malloc(big_len);
    if (!big) return 1;
    /* More than one read chunk, and compressible, so the inflate loop turns
     * more than once each way. */
    for (i = 0; i < big_len; i++) big[i] = (unsigned char)((i * 7) % 251);
    members[0].name = "notes/small.txt";
    members[0].method = 0;
    members[0].data = small;
    members[0].len = sizeof(small) - 1;
    members[1].name = "model/model.onnx";
    members[1].method = 8;
    members[1].data = big;
    members[1].len = big_len;
    expect_true("an archive is written", write_zip(archive, members, 2) == 0);

    expect_true("a member is found", n_zip_find(archive, "model/model.onnx", &info) == 0 && info.method == 8 &&
                                         info.size == big_len);
    expect_true("a missing one is not", n_zip_find(archive, "model/other.onnx", NULL) == -1);
    expect_true("a stored member comes out as it went in",
                n_zip_extract(archive, "notes/small.txt", out) == 0 && file_is(out, small, sizeof(small) - 1));
    expect_true("a deflated one too", n_zip_extract(archive, "model/model.onnx", out) == 0 && file_is(out, big, big_len));
    expect_true("a missing one is refused", n_zip_extract(archive, "nothing", out) == -1);
    {
        unsigned char* data = NULL;
        size_t len = 0;
        char name[64];

        expect_true("a member is read into memory as it went in",
                    n_zip_read(archive, "model/model.onnx", big_len, &data, &len) == 0 && len == big_len &&
                        memcmp(data, big, big_len) == 0 && data[len] == 0);
        free(data);
        data = NULL;
        expect_true("one larger than allowed is refused",
                    n_zip_read(archive, "model/model.onnx", big_len - 1, &data, &len) == -1 && !data);
        expect_true("a member is found by how its name ends",
                    n_zip_find_ending(archive, "/small.txt", name, sizeof(name)) == 0 &&
                        strcmp(name, "notes/small.txt") == 0);
        expect_true("an ending nothing has is not", n_zip_find_ending(archive, "/none.txt", name, sizeof(name)) == -1);
    }
    {
        /* One byte of the deflated data changed: the CRC-32 says so, and the
         * output is not left behind. */
        FILE* f = fopen(archive, "r+b");

        if (f) {
            unsigned char b = 0;

            fseek(f, 30 + 15 + (long)(sizeof(small) - 1) + 30 + 16 + 100, SEEK_SET);
            if (fread(&b, 1, 1, f) == 1) {
                b ^= 0x5a;
                fseek(f, -1, SEEK_CUR);
                fwrite(&b, 1, 1, f);
            }
            fclose(f);
        }
        expect_true("a damaged member is refused and removed",
                    n_zip_extract(archive, "model/model.onnx", out) == -1 && access(out, F_OK) != 0);
    }
    expect_true("a file that is not an archive is refused", n_zip_find("ex_zip.c", "x", NULL) == -1);
    remove(archive);
    remove(out);
    free(big);
    if (failures) {
        fprintf(stderr, "ex_zip: %d failure(s)\n", failures);
        return 1;
    }
    printf("ex_zip: ok\n");
    return 0;
}