← All tasks
cppclaude-code/cpp-t1 #36Not a task: already works

ZIP Archive Manager (cpp, written by Claude Code)

envgap__claude-code__cpp-t1-36

Written by a coding agent; not on GitHubWritten 2026-02-28

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
CMakeLists.txt
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/cpp-t1 #36 · read the task the agent was given
Claude Code wrote this cpp project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: ZIP Archive Manager

Write a program that creates, extracts, lists, and modifies ZIP archives with support for compression levels, password protection, file filtering, and split archives.

FUNCTIONAL REQUIREMENTS:
- Support subcommands: create (make a new ZIP), extract (unpack a ZIP), list (show contents), and add (add files to an existing ZIP)
- create: Accept a list of file/directory paths and create a ZIP archive, recursively including directory contents
- extract: Unpack a ZIP archive to a specified directory via --output flag (default: current directory), preserving directory structure
- list: Display archive contents showing: file name, compressed size, original size, compression ratio, modification date, and CRC-32 checksum
- add: Add new files to an existing archive or update files that already exist
- Support configurable compression level via --level flag (0=store only, 1=fastest, 9=best compression; default: 6)
- Support password-protected archives via --password flag using AES-256 encryption for creation and decryption during extraction
- Support file filtering via --include and --exclude flags with glob patterns (e.g., --include "*.txt" --exclude "*.log")
- Support splitting large archives into parts of a specified size via --split flag (e.g., --split 100m for 100MB parts)
- Verify archive integrity via --verify flag: check CRC-32 checksums for all files without extracting
- Print operation summary to console: total files, total original size, total compressed size, overall compression ratio, and time taken
- Save operation log as JSON with --log flag
- If no arguments are given, create a sample directory structure with various file types and sizes, create a ZIP archive from it, list the contents, extract to a new location, and verify the extraction matches the originals by comparing checksums
- Handle errors: corrupted archives, wrong passwords, disk full during extraction, duplicate entries, and path traversal prevention

Create a complete C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include:
- Source code
- CMakeLists.txt with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(zip_archive_manager VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

# Find libzip
find_package(PkgConfig)
if(PkgConfig_FOUND)
    pkg_check_modules(LIBZIP libzip)
endif()

if(NOT LIBZIP_FOUND)
    FetchContent_Declare(
        libzip
        GIT_REPOSITORY https://github.com/nih-at/libzip.git
        GIT_TAG v1.10.1
    )
    FetchContent_MakeAvailable(libzip)
endif()

# Fetch CLI11
FetchContent_Declare(
    CLI11
    GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
    GIT_TAG v2.3.2
)
FetchContent_MakeAvailable(CLI11)

add_executable(zip_manager zip_manager.cpp)

target_link_libraries(zip_manager PRIVATE
    zip
    CLI11::CLI11
)

if(LIBZIP_FOUND)
    target_include_directories(zip_manager PRIVATE ${LIBZIP_INCLUDE_DIRS})
endif()
README.md
# ZIP Archive Manager (C++ - Trial 1)

Creates, extracts, and lists ZIP archives with compression levels, password protection (AES-256), and directory traversal support.

## Dependencies

- **libzip** (v1.10.1) - C library for reading, creating, and modifying ZIP archives
- **CLI11** (v2.3.2) - Header-only C++ command-line parser with subcommand support

## Build

```bash
mkdir build && cd build
cmake ..
cmake --build .
```

## Usage

```bash
# Create a ZIP archive
./zip_manager create archive.zip file1.txt dir/

# Create with password encryption
./zip_manager create archive.zip file1.txt -p mypassword

# Create with max compression
./zip_manager create archive.zip file1.txt -l 9

# Extract an archive
./zip_manager extract archive.zip -o output_dir/

# Extract specific files
./zip_manager extract archive.zip -f file1.txt file2.txt

# List archive contents
./zip_manager list archive.zip

# Detailed listing
./zip_manager list archive.zip -d
```

## Features

- Create, extract, and list ZIP archives
- AES-256 password encryption
- Configurable compression levels (1-9)
- Recursive directory archiving
- Path traversal protection during extraction
- Detailed and summary listing modes
zip_manager.cpp
/**
 * ZIP Archive Manager
 *
 * Creates, extracts, and lists ZIP archives with compression levels,
 * password protection, and directory traversal support.
 *
 * Dependencies: libzip, CLI11
 */

#include <zip.h>
#include <CLI/CLI.hpp>

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <filesystem>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <algorithm>

namespace fs = std::filesystem;

struct FileEntry {
    std::string name;
    uint64_t size;
    uint64_t compressedSize;
    bool isDirectory;
    std::string method;
    time_t modTime;
    uint32_t crc;
};

std::string formatSize(uint64_t bytes) {
    const char* units[] = {"B", "KB", "MB", "GB", "TB"};
    double size = static_cast<double>(bytes);
    int unit = 0;
    while (size >= 1024.0 && unit < 4) {
        size /= 1024.0;
        unit++;
    }
    std::ostringstream oss;
    oss << std::fixed << std::setprecision(1) << size << " " << units[unit];
    return oss.str();
}

int createArchive(const std::string& archivePath, const std::vector<std::string>& sources,
                  int compressionLevel, const std::string& password, bool verbose) {
    int errcode = 0;
    zip_t* archive = zip_open(archivePath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &errcode);
    if (!archive) {
        std::cerr << "Error: Cannot create archive '" << archivePath << "'" << std::endl;
        return 1;
    }

    if (!password.empty()) {
        zip_set_default_password(archive, password.c_str());
    }

    int fileCount = 0;
    uint64_t totalSize = 0;

    auto addFile = [&](const fs::path& filePath, const std::string& entryName) {
        zip_source_t* source = zip_source_file(archive, filePath.string().c_str(), 0, -1);
        if (!source) {
            std::cerr << "Warning: Cannot read '" << filePath << "', skipping." << std::endl;
            return;
        }

        zip_int64_t index = zip_file_add(archive, entryName.c_str(), source, ZIP_FL_OVERWRITE);
        if (index < 0) {
            std::cerr << "Warning: Cannot add '" << entryName << "': "
                      << zip_strerror(archive) << std::endl;
            zip_source_free(source);
            return;
        }

        zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, compressionLevel);

        if (!password.empty()) {
            zip_file_set_encryption(archive, index, ZIP_EM_AES_256, password.c_str());
        }

        if (verbose) {
            std::cout << "  Adding: " << entryName << std::endl;
        }

        fileCount++;
        totalSize += fs::file_size(filePath);
    };

    for (const auto& src : sources) {
        fs::path sourcePath(src);
        if (!fs::exists(sourcePath)) {
            std::cerr << "Warning: '" << src << "' not found, skipping." << std::endl;
            continue;
        }

        if (fs::is_directory(sourcePath)) {
            std::string baseName = sourcePath.filename().string();
            zip_dir_add(archive, (baseName + "/").c_str(), ZIP_FL_ENC_UTF_8);

            for (const auto& entry : fs::recursive_directory_iterator(sourcePath)) {
                std::string relativePath = baseName + "/" +
                    fs::relative(entry.path(), sourcePath).string();
                std::replace(relativePath.begin(), relativePath.end(), '\\', '/');

                if (entry.is_directory()) {
                    zip_dir_add(archive, (relativePath + "/").c_str(), ZIP_FL_ENC_UTF_8);
                } else if (entry.is_regular_file()) {
                    addFile(entry.path(), relativePath);
                }
            }
        } else if (fs::is_regular_file(sourcePath)) {
            addFile(sourcePath, sourcePath.filename().string());
        }
    }

    if (zip_close(archive) < 0) {
        std::cerr << "Error: Failed to finalize archive: " << zip_strerror(archive) << std::endl;
        return 1;
    }

    uint64_t archiveSize = fs::file_size(archivePath);
    std::cout << "Created archive: " << archivePath << " (" << fileCount << " files, "
              << formatSize(totalSize) << " -> " << formatSize(archiveSize) << ")" << std::endl;
    return 0;
}

int extractArchive(const std::string& archivePath, const std::string& outputDir,
                   const std::string& password, const std::vector<std::string>& specificFiles,
                   bool verbose) {
    int errcode = 0;
    zip_t* archive = zip_open(archivePath.c_str(), ZIP_RDONLY, &errcode);
    if (!archive) {
        std::cerr << "Error: Cannot open archive '" << archivePath << "'" << std::endl;
        return 1;
    }

    if (!password.empty()) {
        zip_set_default_password(archive, password.c_str());
    }

    fs::create_directories(outputDir);
    int fileCount = 0;
    zip_int64_t numEntries = zip_get_num_entries(archive, 0);

    for (zip_int64_t i = 0; i < numEntries; ++i) {
        struct zip_stat st;
        zip_stat_init(&st);
        zip_stat_index(archive, i, 0, &st);

        std::string entryName(st.name);

        if (!specificFiles.empty()) {
            bool found = false;
            for (const auto& f : specificFiles) {
                if (entryName == f) { found = true; break; }
            }
            if (!found) continue;
        }

        fs::path destPath = fs::path(outputDir) / entryName;

        // Security: prevent path traversal
        fs::path normalDest = destPath.lexically_normal();
        fs::path normalOut = fs::path(outputDir).lexically_normal();
        if (normalDest.string().find(normalOut.string()) != 0) {
            std::cerr << "Warning: Skipping entry with path traversal: "
                      << entryName << std::endl;
            continue;
        }

        if (entryName.back() == '/') {
            fs::create_directories(destPath);
            if (verbose) {
                std::cout << "  Directory: " << entryName << std::endl;
            }
        } else {
            fs::create_directories(destPath.parent_path());
            zip_file_t* zf = zip_fopen_index(archive, i, 0);
            if (!zf) {
                std::cerr << "Warning: Cannot extract '" << entryName << "'" << std::endl;
                continue;
            }

            std::ofstream outFile(destPath, std::ios::binary);
            char buffer[8192];
            zip_int64_t bytesRead;
            while ((bytesRead = zip_fread(zf, buffer, sizeof(buffer))) > 0) {
                outFile.write(buffer, bytesRead);
            }

            zip_fclose(zf);
            outFile.close();
            fileCount++;

            if (verbose) {
                std::cout << "  Extracting: " << entryName << " ("
                          << formatSize(st.size) << ")" << std::endl;
            }
        }
    }

    zip_close(archive);
    std::cout << "Extracted " << fileCount << " file(s) to: " << outputDir << std::endl;
    return 0;
}

int listArchive(const std::string& archivePath, const std::string& password, bool detailed) {
    int errcode = 0;
    zip_t* archive = zip_open(archivePath.c_str(), ZIP_RDONLY, &errcode);
    if (!archive) {
        std::cerr << "Error: Cannot open archive '" << archivePath << "'" << std::endl;
        return 1;
    }

    if (!password.empty()) {
        zip_set_default_password(archive, password.c_str());
    }

    std::vector<FileEntry> entries;
    zip_int64_t numEntries = zip_get_num_entries(archive, 0);

    for (zip_int64_t i = 0; i < numEntries; ++i) {
        struct zip_stat st;
        zip_stat_init(&st);
        zip_stat_index(archive, i, 0, &st);

        FileEntry entry;
        entry.name = st.name;
        entry.size = st.size;
        entry.compressedSize = st.comp_size;
        entry.isDirectory = (entry.name.back() == '/');
        entry.method = (st.comp_method == ZIP_CM_STORE) ? "STORE" : "DEFLATE";
        entry.modTime = st.mtime;
        entry.crc = st.crc;
        entries.push_back(entry);
    }

    zip_close(archive);

    if (entries.empty()) {
        std::cout << "Archive is empty." << std::endl;
        return 0;
    }

    if (detailed) {
        std::cout << std::left << std::setw(40) << "Filename"
                  << std::right << std::setw(12) << "Size"
                  << std::setw(12) << "Compressed"
                  << std::setw(8) << "Ratio"
                  << std::setw(10) << "Method"
                  << std::setw(10) << "CRC-32" << std::endl;
        std::cout << std::string(92, '-') << std::endl;

        uint64_t totalSize = 0, totalCompressed = 0;
        int fileCount = 0, dirCount = 0;

        for (const auto& e : entries) {
            if (e.isDirectory) {
                std::cout << std::left << std::setw(40) << e.name
                          << std::right << std::setw(12) << "<DIR>" << std::endl;
                dirCount++;
            } else {
                double ratio = e.size > 0 ?
                    (1.0 - (double)e.compressedSize / e.size) * 100.0 : 0;
                std::ostringstream crcStr;
                crcStr << std::uppercase << std::hex
                       << std::setw(8) << std::setfill('0') << e.crc;

                std::cout << std::left << std::setw(40) << e.name
                          << std::right << std::setw(12) << e.size
                          << std::setw(12) << e.compressedSize
                          << std::setw(7) << std::fixed << std::setprecision(1)
                          << ratio << "%"
                          << std::setw(10) << e.method
                          << std::setw(10) << crcStr.str() << std::endl;
                totalSize += e.size;
                totalCompressed += e.compressedSize;
                fileCount++;
            }
        }

        std::cout << std::string(92, '-') << std::endl;
        std::cout << "Total: " << fileCount << " file(s), " << dirCount
                  << " directory(ies), " << formatSize(totalSize)
                  << " -> " << formatSize(totalCompressed) << std::endl;
    } else {
        std::cout << std::left << std::setw(50) << "Filename"
                  << std::right << std::setw(12) << "Size" << std::endl;
        std::cout << std::string(64, '-') << std::endl;

        for (const auto& e : entries) {
            std::cout << std::left << std::setw(50) << e.name
                      << std::right << std::setw(12)
                      << (e.isDirectory ? "<DIR>" : std::to_string(e.size))
                      << std::endl;
        }

        std::cout << "\nTotal: " << entries.size() << " entries" << std::endl;
    }

    return 0;
}

int main(int argc, char* argv[]) {
    CLI::App app{"ZIP Archive Manager"};
    app.require_subcommand(1);

    // Create subcommand
    auto createCmd = app.add_subcommand("create", "Create a ZIP archive");
    std::string createPath;
    std::vector<std::string> createSources;
    int compressionLevel = 6;
    std::string createPassword;
    bool createVerbose = false;

    createCmd->add_option("archive", createPath, "Archive path")->required();
    createCmd->add_option("sources", createSources, "Source files/directories")->required();
    createCmd->add_option("-l,--level", compressionLevel, "Compression level (1-9)");
    createCmd->add_option("-p,--password", createPassword, "Password for encryption");
    createCmd->add_flag("-v,--verbose", createVerbose, "Verbose output");

    // Extract subcommand
    auto extractCmd = app.add_subcommand("extract", "Extract a ZIP archive");
    std::string extractPath;
    std::string extractOutput = ".";
    std::string extractPassword;
    std::vector<std::string> extractFiles;
    bool extractVerbose = false;

    extractCmd->add_option("archive", extractPath, "Archive path")->required();
    extractCmd->add_option("-o,--output", extractOutput, "Output directory");
    extractCmd->add_option("-p,--password", extractPassword, "Password");
    extractCmd->add_option("-f,--files", extractFiles, "Specific files to extract");
    extractCmd->add_flag("-v,--verbose", extractVerbose, "Verbose output");

    // List subcommand
    auto listCmd = app.add_subcommand("list", "List archive contents");
    std::string listPath;
    std::string listPassword;
    bool listDetailed = false;

    listCmd->add_option("archive", listPath, "Archive path")->required();
    listCmd->add_option("-p,--password", listPassword, "Password");
    listCmd->add_flag("-d,--detailed", listDetailed, "Detailed listing");

    CLI11_PARSE(app, argc, argv);

    if (createCmd->parsed()) {
        return createArchive(createPath, createSources, compressionLevel,
                           createPassword, createVerbose);
    } else if (extractCmd->parsed()) {
        return extractArchive(extractPath, extractOutput, extractPassword,
                            extractFiles, extractVerbose);
    } else if (listCmd->parsed()) {
        return listArchive(listPath, listPassword, listDetailed);
    }

    return 0;
}