← All tasks
cppclaude-code/cpp-t3 #38Not a task: not reproduced

Tar Archive Handler (cpp, written by Claude Code)

envgap__claude-code__cpp-t3-38

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

01 / FAILURE SIGNATURE

As the study recorded it

Could NOT find LibLZMA
Not a benchmark task.
  • In a clean container the reported failure did not reproduce, or the known fix did not make the project run.

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-t3 #38 · read the task the agent was given
Claude Code wrote this cpp project from the task below. It does not run on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Tar Archive Handler

Write a program that creates, extracts, lists, and manipulates TAR archives with support for multiple compression methods (gzip, bzip2, xz), preserving file permissions and ownership metadata.

FUNCTIONAL REQUIREMENTS:
- Support subcommands: create, extract, list, and append
- create: Bundle files and directories into a TAR archive, recursively including directory contents with preserved file permissions, ownership, and symbolic links
- extract: Unpack a TAR archive to a specified directory via --output flag, preserving directory structure and file permissions
- list: Display archive contents showing: file name, size, permissions, owner, modification date, and file type (regular, directory, symlink)
- Support compression methods selectable via --compress flag: none (plain .tar), gzip (.tar.gz), bzip2 (.tar.bz2), xz (.tar.xz); auto-detect during extraction
- append: Add files to an existing uncompressed TAR archive
- Support file filtering via --include and --exclude flags with glob patterns
- Support extracting specific files from an archive by listing their paths as additional arguments
- Verify archive integrity via --verify flag
- Print operation summary to console: total files, total size, compression ratio (if compressed), and processing time
- Save operation log as JSON with --log flag
- If no arguments are given, create a sample directory structure with files of various types, create archives in each compression format, list contents of each, extract one, and compare compression ratios
- Handle errors: corrupted archives, unsupported formats, path traversal attacks, and permission issues during extraction

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(TarArchiveHandler VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

# Find LZMA/XZ Utils
find_package(LibLZMA REQUIRED)

# Fetch fmt library
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG 10.2.1
)
FetchContent_MakeAvailable(fmt)

add_executable(tar_handler main.cpp)

target_link_libraries(tar_handler
    PRIVATE
        LibLZMA::LibLZMA
        fmt::fmt
)

install(TARGETS tar_handler DESTINATION bin)
main.cpp
/**
 * Tar Archive Handler
 * Creates/extracts TAR archives with LZMA/XZ compression.
 * Uses lzma-sdk for compression and fmt for formatted output.
 */

#include <fmt/core.h>
#include <fmt/format.h>
#include <fmt/color.h>
#include <lzma.h>

#include <iostream>
#include <fstream>
#include <filesystem>
#include <vector>
#include <string>
#include <cstring>
#include <cstdint>
#include <ctime>
#include <algorithm>
#include <map>
#include <numeric>

namespace fs = std::filesystem;

static const size_t TAR_BLOCK = 512;
static const size_t LZMA_BUF = 65536;

std::string formatSize(uintmax_t bytes) {
    const char* units[] = {"B", "KB", "MB", "GB", "TB"};
    double size = static_cast<double>(bytes);
    int idx = 0;
    while (size >= 1024.0 && idx < 4) { size /= 1024.0; idx++; }
    return fmt::format("{:.1f} {}", size, units[idx]);
}

struct TarHeader {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char typeflag;
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char prefix[155];
    char padding[12];
};

static_assert(sizeof(TarHeader) == TAR_BLOCK, "TarHeader must be 512 bytes");

void setOctalField(char* field, size_t len, uintmax_t val) {
    std::string s = fmt::format("{:o}", val);
    size_t maxLen = len - 1;
    std::memset(field, '0', maxLen);
    if (s.size() <= maxLen) {
        std::memcpy(field + maxLen - s.size(), s.c_str(), s.size());
    }
    field[maxLen] = '\0';
}

uintmax_t parseOctalField(const char* field, size_t len) {
    std::string s(field, len);
    s.erase(std::remove(s.begin(), s.end(), '\0'), s.end());
    s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
    if (s.empty()) return 0;
    return std::stoull(s, nullptr, 8);
}

void calcChecksum(TarHeader& hdr) {
    std::memset(hdr.checksum, ' ', 8);
    unsigned sum = 0;
    auto* raw = reinterpret_cast<const unsigned char*>(&hdr);
    for (size_t i = 0; i < TAR_BLOCK; i++) sum += raw[i];
    setOctalField(hdr.checksum, 7, sum);
    hdr.checksum[7] = ' ';
}

std::vector<uint8_t> lzmaCompress(const std::vector<uint8_t>& input) {
    lzma_stream strm = LZMA_STREAM_INIT;
    lzma_ret ret = lzma_easy_encoder(&strm, 6, LZMA_CHECK_CRC64);
    if (ret != LZMA_OK) {
        throw std::runtime_error(fmt::format("LZMA encoder init failed: {}", static_cast<int>(ret)));
    }

    std::vector<uint8_t> output;
    output.resize(input.size() + input.size() / 10 + 1024);

    strm.next_in = input.data();
    strm.avail_in = input.size();
    strm.next_out = output.data();
    strm.avail_out = output.size();

    ret = lzma_code(&strm, LZMA_FINISH);
    if (ret != LZMA_STREAM_END) {
        lzma_end(&strm);
        throw std::runtime_error(fmt::format("LZMA compression failed: {}", static_cast<int>(ret)));
    }

    output.resize(strm.total_out);
    lzma_end(&strm);
    return output;
}

std::vector<uint8_t> lzmaDecompress(const std::vector<uint8_t>& input) {
    lzma_stream strm = LZMA_STREAM_INIT;
    lzma_ret ret = lzma_auto_decoder(&strm, UINT64_MAX, 0);
    if (ret != LZMA_OK) {
        throw std::runtime_error(fmt::format("LZMA decoder init failed: {}", static_cast<int>(ret)));
    }

    std::vector<uint8_t> output;
    std::vector<uint8_t> buf(LZMA_BUF);

    strm.next_in = input.data();
    strm.avail_in = input.size();

    do {
        strm.next_out = buf.data();
        strm.avail_out = buf.size();
        ret = lzma_code(&strm, LZMA_FINISH);
        size_t produced = buf.size() - strm.avail_out;
        output.insert(output.end(), buf.data(), buf.data() + produced);
    } while (ret == LZMA_OK);

    lzma_end(&strm);
    if (ret != LZMA_STREAM_END) {
        throw std::runtime_error(fmt::format("LZMA decompression failed: {}", static_cast<int>(ret)));
    }
    return output;
}

void collectFiles(const fs::path& root, const fs::path& base,
                  std::vector<std::pair<fs::path, std::string>>& entries) {
    if (fs::is_regular_file(root)) {
        entries.emplace_back(root, root.filename().generic_string());
    } else if (fs::is_directory(root)) {
        for (auto& e : fs::recursive_directory_iterator(root)) {
            if (e.is_regular_file()) {
                entries.emplace_back(e.path(), fs::relative(e.path(), base).generic_string());
            }
        }
    }
}

int createArchive(const std::string& archivePath, const std::vector<std::string>& inputs,
                  bool useXz, bool verbose) {
    std::vector<std::pair<fs::path, std::string>> entries;
    for (auto& inp : inputs) {
        fs::path p(inp);
        if (!fs::exists(p)) {
            fmt::print(stderr, fg(fmt::color::yellow), "Warning: '{}' not found, skipping.\n", inp);
            continue;
        }
        collectFiles(p, p.parent_path(), entries);
    }

    if (entries.empty()) {
        fmt::print(stderr, fg(fmt::color::red), "Error: No files to archive.\n");
        return 1;
    }

    std::vector<uint8_t> tarData;
    uintmax_t totalOrigSize = 0;
    int fileCount = 0;

    for (auto& [fullPath, arcName] : entries) {
        auto fsize = fs::file_size(fullPath);
        auto perms = fs::status(fullPath).permissions();
        auto ftime = fs::last_write_time(fullPath);
        auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
            std::chrono::clock_cast<std::chrono::system_clock>(ftime));
        time_t mt = sctp.time_since_epoch().count();

        TarHeader hdr{};
        std::strncpy(hdr.name, arcName.c_str(), 99);
        setOctalField(hdr.mode, 8, static_cast<uintmax_t>(perms) & 0777);
        setOctalField(hdr.uid, 8, 0);
        setOctalField(hdr.gid, 8, 0);
        setOctalField(hdr.size, 12, fsize);
        setOctalField(hdr.mtime, 12, static_cast<uintmax_t>(mt));
        hdr.typeflag = '0';
        std::memcpy(hdr.magic, "ustar", 5);
        hdr.version[0] = '0'; hdr.version[1] = '0';
        calcChecksum(hdr);

        auto* hdrBytes = reinterpret_cast<const uint8_t*>(&hdr);
        tarData.insert(tarData.end(), hdrBytes, hdrBytes + TAR_BLOCK);

        std::ifstream ifs(fullPath, std::ios::binary);
        std::vector<uint8_t> fileData((std::istreambuf_iterator<char>(ifs)),
                                       std::istreambuf_iterator<char>());
        tarData.insert(tarData.end(), fileData.begin(), fileData.end());

        size_t rem = TAR_BLOCK - (fileData.size() % TAR_BLOCK);
        if (rem < TAR_BLOCK) tarData.insert(tarData.end(), rem, 0);

        totalOrigSize += fsize;
        fileCount++;

        if (verbose) {
            fmt::print("  Adding: {} ({})\n", arcName, formatSize(fsize));
        }
    }

    tarData.insert(tarData.end(), TAR_BLOCK * 2, 0);

    std::ofstream ofs(archivePath, std::ios::binary);
    if (useXz) {
        auto compressed = lzmaCompress(tarData);
        ofs.write(reinterpret_cast<const char*>(compressed.data()), compressed.size());
    } else {
        ofs.write(reinterpret_cast<const char*>(tarData.data()), tarData.size());
    }
    ofs.close();

    auto archiveSize = fs::file_size(archivePath);
    double ratio = totalOrigSize > 0
        ? (1.0 - static_cast<double>(archiveSize) / totalOrigSize) * 100.0 : 0.0;

    fmt::print(fg(fmt::color::green), "\nArchive created: {}\n", archivePath);
    fmt::print("  Files:             {}\n", fileCount);
    fmt::print("  Original size:     {}\n", formatSize(totalOrigSize));
    fmt::print("  Archive size:      {}\n", formatSize(archiveSize));
    fmt::print("  Compression:       {}\n", useXz ? "xz/lzma" : "none");
    fmt::print("  Compression ratio: {:.1f}%\n", ratio);
    return 0;
}

int extractArchive(const std::string& archivePath, const std::string& outputDir,
                   const std::string& filter, bool verbose) {
    if (!fs::exists(archivePath)) {
        fmt::print(stderr, fg(fmt::color::red), "Error: '{}' not found.\n", archivePath);
        return 1;
    }

    fs::create_directories(outputDir);

    std::ifstream ifs(archivePath, std::ios::binary);
    std::vector<uint8_t> raw((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    ifs.close();

    bool isXz = archivePath.ends_with(".xz") || archivePath.ends_with(".txz");
    std::vector<uint8_t> tarData = isXz ? lzmaDecompress(raw) : std::move(raw);

    size_t pos = 0;
    int count = 0;

    while (pos + TAR_BLOCK <= tarData.size()) {
        auto* hdr = reinterpret_cast<const TarHeader*>(tarData.data() + pos);
        if (hdr->name[0] == '\0') break;

        std::string name(hdr->name);
        uintmax_t fileSize = parseOctalField(hdr->size, 12);
        pos += TAR_BLOCK;

        if (!filter.empty() && name.find(filter) == std::string::npos) {
            size_t blocks = (fileSize + TAR_BLOCK - 1) / TAR_BLOCK;
            pos += blocks * TAR_BLOCK;
            continue;
        }

        fs::path outPath = fs::path(outputDir) / name;
        fs::create_directories(outPath.parent_path());

        if (hdr->typeflag == '5') {
            fs::create_directories(outPath);
        } else {
            std::ofstream ofs(outPath, std::ios::binary);
            if (fileSize > 0 && pos + fileSize <= tarData.size()) {
                ofs.write(reinterpret_cast<const char*>(tarData.data() + pos), fileSize);
            }
            count++;
            if (verbose) {
                fmt::print("  {} ({})\n", name, formatSize(fileSize));
            }
        }

        size_t blocks = (fileSize + TAR_BLOCK - 1) / TAR_BLOCK;
        pos += blocks * TAR_BLOCK;
    }

    fmt::print(fg(fmt::color::green), "Extracted {} file(s) to '{}'\n", count, outputDir);
    return 0;
}

int listArchive(const std::string& archivePath, bool detailed) {
    if (!fs::exists(archivePath)) {
        fmt::print(stderr, fg(fmt::color::red), "Error: '{}' not found.\n", archivePath);
        return 1;
    }

    std::ifstream ifs(archivePath, std::ios::binary);
    std::vector<uint8_t> raw((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    ifs.close();

    bool isXz = archivePath.ends_with(".xz") || archivePath.ends_with(".txz");
    std::vector<uint8_t> tarData = isXz ? lzmaDecompress(raw) : std::move(raw);

    if (detailed) {
        fmt::print("{:<10} {:>12}  {:<20} {}\n", "Type", "Size", "Modified", "Name");
        fmt::print("{}\n", std::string(80, '-'));
    }

    size_t pos = 0;
    uintmax_t totalSize = 0;
    int fileCount = 0, dirCount = 0;

    while (pos + TAR_BLOCK <= tarData.size()) {
        auto* hdr = reinterpret_cast<const TarHeader*>(tarData.data() + pos);
        if (hdr->name[0] == '\0') break;

        std::string name(hdr->name);
        uintmax_t fileSize = parseOctalField(hdr->size, 12);
        uintmax_t mtime = parseOctalField(hdr->mtime, 12);
        bool isDir = hdr->typeflag == '5';

        if (isDir) dirCount++; else fileCount++;
        totalSize += fileSize;

        if (detailed) {
            std::string type = isDir ? "directory" : "file";
            time_t mt = static_cast<time_t>(mtime);
            char timeBuf[20];
            strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M", localtime(&mt));
            fmt::print("{:<10} {:>12}  {:<20} {}\n", type, formatSize(fileSize), timeBuf, name);
        } else {
            fmt::print("{}\n", name);
        }

        pos += TAR_BLOCK;
        size_t blocks = (fileSize + TAR_BLOCK - 1) / TAR_BLOCK;
        pos += blocks * TAR_BLOCK;
    }

    if (detailed) fmt::print("{}\n", std::string(80, '-'));
    fmt::print("Total: {} files, {} directories | {}\n", fileCount, dirCount, formatSize(totalSize));
    return 0;
}

int inspectArchive(const std::string& archivePath) {
    if (!fs::exists(archivePath)) {
        fmt::print(stderr, fg(fmt::color::red), "Error: '{}' not found.\n", archivePath);
        return 1;
    }

    auto archFileSize = fs::file_size(archivePath);
    std::ifstream ifs(archivePath, std::ios::binary);
    std::vector<uint8_t> raw((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    ifs.close();

    bool isXz = archivePath.ends_with(".xz") || archivePath.ends_with(".txz");
    std::vector<uint8_t> tarData = isXz ? lzmaDecompress(raw) : std::move(raw);

    size_t pos = 0;
    uintmax_t totalSize = 0;
    int fileCount = 0, dirCount = 0;
    std::map<std::string, int> extensions;
    std::string largestName;
    uintmax_t largestSize = 0;

    while (pos + TAR_BLOCK <= tarData.size()) {
        auto* hdr = reinterpret_cast<const TarHeader*>(tarData.data() + pos);
        if (hdr->name[0] == '\0') break;

        std::string name(hdr->name);
        uintmax_t fileSize = parseOctalField(hdr->size, 12);
        totalSize += fileSize;

        if (hdr->typeflag == '5') {
            dirCount++;
        } else {
            fileCount++;
            auto dot = name.rfind('.');
            std::string ext = (dot != std::string::npos) ? name.substr(dot) : "(none)";
            extensions[ext]++;
            if (fileSize > largestSize) { largestSize = fileSize; largestName = name; }
        }

        pos += TAR_BLOCK;
        size_t blocks = (fileSize + TAR_BLOCK - 1) / TAR_BLOCK;
        pos += blocks * TAR_BLOCK;
    }

    double ratio = totalSize > 0
        ? (1.0 - static_cast<double>(archFileSize) / totalSize) * 100.0 : 0.0;

    fmt::print(fg(fmt::color::cyan), "\n=== Archive Info: {} ===\n", archivePath);
    fmt::print("  Archive size:      {}\n", formatSize(archFileSize));
    fmt::print("  Uncompressed size: {}\n", formatSize(totalSize));
    fmt::print("  Entries:           {}\n", fileCount + dirCount);
    fmt::print("  Files:             {}\n", fileCount);
    fmt::print("  Directories:       {}\n", dirCount);
    fmt::print("  Compression ratio: {:.1f}%\n", ratio);

    if (!largestName.empty()) {
        fmt::print("  Largest file:      {} ({})\n", largestName, formatSize(largestSize));
    }
    if (!extensions.empty()) {
        fmt::print("\n  File types:\n");
        for (auto& [ext, cnt] : extensions) {
            fmt::print("    {}: {}\n", ext, cnt);
        }
    }
    return 0;
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        fmt::print("Tar Archive Handler (lzma-sdk + fmt)\n");
        fmt::print("Usage:\n");
        fmt::print("  tar_handler create <archive> <file1> [file2...] [--xz] [-v]\n");
        fmt::print("  tar_handler extract <archive> [-o dir] [-f filter] [-v]\n");
        fmt::print("  tar_handler list <archive> [-d]\n");
        fmt::print("  tar_handler inspect <archive>\n");
        return 1;
    }

    std::string command = argv[1];
    bool verbose = false, detailed = false, useXz = false;
    std::string archivePath, outputDir = ".", filter;
    std::vector<std::string> files;

    for (int i = 2; i < argc; i++) {
        std::string arg = argv[i];
        if (arg == "-v" || arg == "--verbose") verbose = true;
        else if (arg == "-d" || arg == "--detailed") detailed = true;
        else if (arg == "--xz") useXz = true;
        else if ((arg == "-o" || arg == "--output") && i + 1 < argc) outputDir = argv[++i];
        else if ((arg == "-f" || arg == "--filter") && i + 1 < argc) filter = argv[++i];
        else if (archivePath.empty()) archivePath = arg;
        else files.push_back(arg);
    }

    if (command == "create") return createArchive(archivePath, files, useXz, verbose);
    if (command == "extract") return extractArchive(archivePath, outputDir, filter, verbose);
    if (command == "list") return listArchive(archivePath, detailed);
    if (command == "inspect") return inspectArchive(archivePath);

    fmt::print(stderr, fg(fmt::color::red), "Unknown command: {}\n", command);
    return 1;
}
README.md
# Tar Archive Handler (C++ - lzma-sdk + fmt)

A command-line tool for creating and extracting TAR archives with LZMA/XZ compression. Uses the fmt library for colorized, formatted terminal output.

## Dependencies

- **lzma-sdk (liblzma)**: LZMA compression algorithm implementation (part of XZ Utils)
- **fmt**: Modern formatting library for C++

## Building

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

## Usage

```bash
# Create an xz-compressed tar archive
./tar_handler create archive.tar.xz file1.txt dir1/ --xz -v

# Create uncompressed tar archive
./tar_handler create archive.tar file1.txt

# Extract archive
./tar_handler extract archive.tar.xz -o output_dir/ -v

# List archive contents
./tar_handler list archive.tar.xz -d

# Inspect archive metadata
./tar_handler inspect archive.tar.xz
```

## Features

- Manual TAR format implementation with POSIX headers
- LZMA/XZ compression and decompression via liblzma
- Colorized terminal output using fmt library
- Archive inspection with file type statistics
- Permission preservation via TAR headers
- Recursive directory traversal for archive creation
- Detailed listing with sizes, types, and timestamps