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;
}