GZIP Compression Tool (java, written by Claude Code)
envgap__claude-code__java-t2-37
Written by a coding agent; not on GitHubWritten 2026-02-28
01 / FAILURE SIGNATURE
Captured in a clean container
error: no classes were compiled
02 / ENVIRONMENT RECIPE
- Base commit
19bce154b5bcdbd38472a37b14eb1994f929ad09- Manifest
pom.xml- Reproduce
jar=$(ls target/*-jar-with-dependencies.jar target/*-shaded.jar target/*-all.jar 2>/dev/null | head -n1); [ -n "$jar" ] || jar=$(ls -S target/*.jar 2>/dev/null | grep -v -e '/original-' -e '-sources.jar$' -e '-javadoc.jar$' -e '-tests.jar$' | head -n1); test -n "$jar" || { echo 'error: no jar was built'; exit 1; }; jarcp=$(python3 -c 'import os, sys, zipfile from urllib.parse import unquote jar = sys.argv[1] try: text = zipfile.ZipFile(jar).read("META-INF/MANIFEST.MF").decode("utf-8", "replace") except (KeyError, OSError, zipfile.BadZipFile): text = "" text = text.replace("\r\n", "\n").replace("\r", "\n").replace("\n ", "") found = [line.split(":", 1)[1].split() for line in text.split("\n") if line.lower().startswith("class-path:")] entries = [os.path.join(os.path.dirname(jar), unquote(entry)) for entry in (found[0] if found else [])] print(":".join([jar] + [entry for entry in entries if os.path.exists(entry)]))' "$jar") || exit 1; test -d target/classes || { echo 'error: no classes were compiled'; exit 1; }; python3 -c 'import hashlib, os, subprocess, sys tracked = [p for p in subprocess.run(["git", "ls-files", "-z", "--", "*.java"], capture_output=True).stdout.decode().split("\0") if p] digest = lambda p: hashlib.sha256(open(p, "rb").read()).hexdigest() own = {digest(p) for p in tracked if os.path.isfile(p)} names = {os.path.basename(p)[:-5] for p in tracked} | {"package-info", "module-info"} bad = [] for top, _, files in os.walk("target"): for name in files: path = os.path.join(top, name) if name.endswith(".java") and digest(path) not in own: bad.append(path) elif top.startswith(os.path.join("target", "classes")) and name.endswith(".class") and name[:-6].split("$")[0] not in names: bad.append(path) if bad: print("\n".join(sorted(bad)[:20])) print("error: the build compiled classes that are not from the project sources") sys.exit(1)' || exit 1; jd=$(jdeps --multi-release 17 -verbose:class -cp "$jarcp" target/classes 2>&1) && st=0 || st=$?; missing=$(printf '%s\n' "$jd" | grep 'not found' || true); if [ $st -ne 0 ]; then printf '%s\n' "$jd" | tail -n 20; echo 'error: jdeps could not read the classes'; exit 1; fi; if [ -n "$missing" ]; then printf '%s\n' "$missing"; echo 'error: classes the program uses are missing from the class path it runs with'; exit 1; fi- Run under trace
jar=$(ls target/*-jar-with-dependencies.jar target/*-shaded.jar target/*-all.jar 2>/dev/null | head -n1); [ -n "$jar" ] || jar=$(ls -S target/*.jar 2>/dev/null | grep -v -e '/original-' -e '-sources.jar$' -e '-javadoc.jar$' -e '-tests.jar$' | head -n1); test -n "$jar" || { echo 'error: no jar was built'; exit 1; }; rc=0; out=$(timeout 60 java -jar "$jar" < /dev/null 2>&1 | { head -c 1000000; cat > /dev/null; }; exit ${PIPESTATUS[0]}) || rc=$?; printf '%s\n' "$out"; env_error='(ModuleNotFoundError|ImportError|No module named|cannot open shared object file|DLL load failed|shared library|cannot load library|Library not loaded|Cannot find module|ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND|ERR_REQUIRE_ESM|compiled against a different Node|Could not find or load main class|ClassNotFoundException|NoClassDefFoundError|UnsupportedClassVersionError|UnsatisfiedLinkError|NoSuchMethodError|NoSuchFieldError|AbstractMethodError|IncompatibleClassChangeError|IllegalAccessError|ServiceConfigurationError|error while loading shared libraries|symbol lookup error|version `[^'"'"']*'"'"' not found|command not found)'; asked='(^| )[[:blank:]]*usage:|the following arguments are required|missing (required )?(argument|option|operand|parameter)|eoferror: eof when reading a line|please (provide|specify|enter)|no (input|file|directory|url|command) (specified|given|provided)'; low=${out,,}; if [ $rc -eq 0 ]; then exit 0; fi; if [ $rc -ge 126 ] || [[ $out =~ $env_error ]]; then exit 1; fi; if [ $rc -eq 124 ] || [[ $low =~ $asked ]]; then exit 0; fi; if [[ $low =~ nosuchelementexception ]] && [[ $low =~ java\.util\.scanner ]]; then exit 0; fi; exit 1
Reference environment fix used for admission
--- /dev/null
+++ b/src/main/java/com/gziptool/GzipTool.java
@@ -0,0 +1,423 @@
+package com.gziptool;
+
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
+import org.apache.commons.compress.compressors.gzip.GzipParameters;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.*;
+import java.nio.file.*;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.zip.CRC32;
+
+/**
+ * GZIP Compression Tool
+ *
+ * A command-line utility for compressing and decompressing files using GZIP/DEFLATE
+ * with streaming support and integrity verification.
+ *
+ * Dependencies:
+ * - Apache Commons Compress (1.25.0): Advanced compression/decompression library.
+ * - Gson (2.10.1): JSON serialization for structured output.
+ */
+public class GzipTool {
+
+ private static final int BUFFER_SIZE = 65536; // 64 KB
+ private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
+
+ public static void main(String[] args) {
+ if (args.length < 1) {
+ printUsage();
+ System.exit(1);
+ }
+
+ String command = args[0].toLowerCase();
+
+ try {
+ switch (command) {
+ case "compress":
+ handleCompress(args);
+ break;
+ case "decompress":
+ handleDecompress(args);
+ break;
+ case "verify":
+ handleVerify(args);
+ break;
+ case "info":
+ handleInfo(args);
+ break;
+ case "--help":
+ case "-h":
+ printUsage();
+ break;
+ default:
+ System.err.println("Unknown command: " + command);
+ printUsage();
+ System.exit(1);
+ }
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ System.exit(1);
+ }
+ }
+
+ private static void printUsage() {
+ System.out.println("GZIP Compression Tool v1.0.0");
+ System.out.println();
+ System.out.println("Usage: java GzipTool <command> [options]");
+ System.out.println();
+ System.out.println("Commands:");
+ System.out.println(" compress <input> [-o output] [-l level] [-f] [-v] [--json]");
+ System.out.println(" decompress <input> [-o output] [-f] [-v]");
+ System.out.println(" verify <input> [-v] [--json]");
+ System.out.println(" info <input> [--json]");
+ }
+
+ private static String parseOption(String[] args, String shortOpt, String longOpt, String defaultVal) {
+ for (int i = 0; i < args.length - 1; i++) {
+ if (args[i].equals(shortOpt) || args[i].equals(longOpt)) {
+ return args[i + 1];
+ }
+ }
+ return defaultVal;
+ }
+
+ private static boolean hasFlag(String[] args, String shortFlag, String longFlag) {
+ for (String arg : args) {
+ if (arg.equals(shortFlag) || arg.equals(longFlag)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void handleCompress(String[] args) throws IOException, NoSuchAlgorithmException {
+ if (args.length < 2) {
+ System.err.println("Usage: compress <input> [-o output] [-l level] [-f] [-v] [--json]");
+ System.exit(1);
+ }
+
+ String inputPath = args[1];
+ File inputFile = new File(inputPath);
+ if (!inputFile.exists()) {
+ System.err.println("Error: Input file not found: " + inputPath);
+ System.exit(1);
+ }
+
+ String outputPath = parseOption(args, "-o", "--output", inputPath + ".gz");
+ int level = Integer.parseInt(parseOption(args, "-l", "--level", "6"));
+ boolean force = hasFlag(args, "-f", "--force");
+ boolean verbose = hasFlag(args, "-v", "--verbose");
+ boolean json = hasFlag(args, "--json", "--json");
+
+ if (level < 1 || level > 9) {
+ System.err.println("Error: Compression level must be between 1 and 9.");
+ System.exit(1);
+ }
+
+ File outputFile = new File(outputPath);
+ if (outputFile.exists() && !force) {
+ System.err.println("Error: Output file '" + outputPath + "' already exists. Use -f to overwrite.");
+ System.exit(1);
+ }
+
+ long inputSize = inputFile.length();
+ if (verbose) {
+ System.out.println("Input: " + inputPath + " (" + formatSize(inputSize) + ")");
+ System.out.println("Output: " + outputPath);
+ System.out.println("Compression level: " + level);
+ }
+
+ long startTime = System.currentTimeMillis();
+
+ GzipParameters params = new GzipParameters();
+ params.setCompressionLevel(level);
+ params.setFilename(inputFile.getName());
+
+ try (InputStream fis = new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE);
+ GzipCompressorOutputStream gzos = new GzipCompressorOutputStream(
+ new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER_SIZE), params)) {
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+ long bytesRead = 0;
+ int len;
+
+ while ((len = fis.read(buffer)) != -1) {
+ gzos.write(buffer, 0, len);
+ bytesRead += len;
+ if (verbose && inputSize > 0) {
+ double progress = (double) bytesRead / inputSize * 100.0;
+ System.out.printf("\rProgress: %.1f%%", progress);
+ }
+ }
+
+ if (verbose) System.out.println();
+ }
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ long outputSize = outputFile.length();
+
+ if (json) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("input", inputPath);
+ result.put("output", outputPath);
+ result.put("input_size", inputSize);
+ result.put("output_size", outputSize);
+ result.put("ratio", inputSize > 0 ? (1.0 - (double) outputSize / inputSize) * 100.0 : 0.0);
+ result.put("level", level);
+ result.put("elapsed_ms", elapsed);
+ result.put("sha256_input", computeSha256(inputFile));
+ result.put("sha256_output", computeSha256(outputFile));
+ System.out.println(GSON.toJson(result));
+ } else if (verbose) {
+ double ratio = inputSize > 0 ? (1.0 - (double) outputSize / inputSize) * 100.0 : 0;
+ System.out.println("Compressed size: " + formatSize(outputSize));
+ System.out.printf("Compression ratio: %.1f%%%n", ratio);
+ System.out.printf("Time: %.3fs%n", elapsed / 1000.0);
+ System.out.println("SHA-256 (input): " + computeSha256(inputFile));
+ System.out.println("SHA-256 (output): " + computeSha256(outputFile));
+ } else {
+ System.out.printf("Compressed '%s' -> '%s' (%s -> %s)%n",
+ inputPath, outputPath, formatSize(inputSize), formatSize(outputSize));
+ }
+ }
+
+ private static void handleDecompress(String[] args) throws IOException, NoSuchAlgorithmException {
+ if (args.length < 2) {
+ System.err.println("Usage: decompress <input> [-o output] [-f] [-v]");
+ System.exit(1);
+ }
+
+ String inputPath = args[1];
+ File inputFile = new File(inputPath);
+ if (!inputFile.exists()) {
+ System.err.println("Error: Input file not found: " + inputPath);
+ System.exit(1);
+ }
+
+ String defaultOutput = inputPath.endsWith(".gz")
+ ? inputPath.substring(0, inputPath.length() - 3)
+ : inputPath + ".out";
+ String outputPath = parseOption(args, "-o", "--output", defaultOutput);
+ boolean force = hasFlag(args, "-f", "--force");
+ boolean verbose = hasFlag(args, "-v", "--verbose");
+
+ File outputFile = new File(outputPath);
+ if (outputFile.exists() && !force) {
+ System.err.println("Error: Output file '" + outputPath + "' already exists. Use -f to overwrite.");
+ System.exit(1);
+ }
+
+ long inputSize = inputFile.length();
+ if (verbose) {
+ System.out.println("Input: " + inputPath + " (" + formatSize(inputSize) + ")");
+ System.out.println("Output: " + outputPath);
+ }
+
+ long startTime = System.currentTimeMillis();
+ long bytesWritten = 0;
+
+ try (GzipCompressorInputStream gzis = new GzipCompressorInputStream(
+ new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE));
+ OutputStream fos = new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER_SIZE)) {
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int len;
+
+ while ((len = gzis.read(buffer)) != -1) {
+ fos.write(buffer, 0, len);
+ bytesWritten += len;
+ if (verbose) {
+ System.out.printf("\rDecompressed: %s", formatSize(bytesWritten));
+ }
+ }
+
+ if (verbose) System.out.println();
+ }
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ long outputSize = outputFile.length();
+
+ if (verbose) {
+ System.out.println("Decompressed size: " + formatSize(outputSize));
+ System.out.printf("Time: %.3fs%n", elapsed / 1000.0);
+ System.out.println("SHA-256 (output): " + computeSha256(outputFile));
+ } else {
+ System.out.printf("Decompressed '%s' -> '%s' (%s -> %s)%n",
+ inputPath, outputPath, formatSize(inputSize), formatSize(outputSize));
+ }
+ }
+
+ private static void handleVerify(String[] args) throws IOException {
+ if (args.length < 2) {
+ System.err.println("Usage: verify <input> [-v] [--json]");
+ System.exit(1);
+ }
+
+ String inputPath = args[1];
+ File inputFile = new File(inputPath);
+ boolean verbose = hasFlag(args, "-v", "--verbose");
+ boolean json = hasFlag(args, "--json", "--json");
+
+ if (!inputFile.exists()) {
+ System.err.println("Error: File not found: " + inputPath);
+ System.exit(1);
+ }
+
+ if (verbose) {
+ System.out.println("Verifying: " + inputPath);
+ }
+
+ // Check magic bytes
+ byte[] header = new byte[2];
+ try (InputStream is = new FileInputStream(inputFile)) {
+ if (is.read(header) < 2 || header[0] != (byte) 0x1f || header[1] != (byte) 0x8b) {
+ System.err.println("FAILED: Invalid GZIP magic bytes");
+ System.exit(1);
+ }
+ }
+
+ CRC32 crc = new CRC32();
+ long totalSize = 0;
+
+ try (GzipCompressorInputStream gzis = new GzipCompressorInputStream(
+ new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE))) {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int len;
+ while ((len = gzis.read(buffer)) != -1) {
+ crc.update(buffer, 0, len);
+ totalSize += len;
+ }
+ } catch (IOException e) {
+ System.err.println("FAILED: " + e.getMessage());
+ System.exit(1);
+ }
+
+ long compressedSize = inputFile.length();
+
+ if (json) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("file", inputPath);
+ result.put("valid", true);
+ result.put("compressed_size", compressedSize);
+ result.put("original_size", totalSize);
+ result.put("crc32", String.format("0x%08X", crc.getValue()));
+ System.out.println(GSON.toJson(result));
+ } else if (verbose) {
+ System.out.println("Compressed size: " + formatSize(compressedSize));
+ System.out.println("Original size: " + formatSize(totalSize));
+ System.out.printf("CRC-32: 0x%08X%n", crc.getValue());
+ System.out.println("CRC-32 match: Yes");
+ System.out.println("Size match: Yes");
+ }
+
+ if (!json) {
+ System.out.printf("OK: '%s' integrity verified successfully.%n", inputPath);
+ }
+ }
+
+ private static void handleInfo(String[] args) throws IOException {
+ if (args.length < 2) {
+ System.err.println("Usage: info <input> [--json]");
+ System.exit(1);
+ }
+
+ String inputPath = args[1];
+ File inputFile = new File(inputPath);
+ boolean json = hasFlag(args, "--json", "--json");
+
+ if (!inputFile.exists()) {
+ System.err.println("Error: File not found: " + inputPath);
+ System.exit(1);
+ }
+
+ long compressedSize = inputFile.length();
+
+ // Read header using Commons Compress
+ GzipParameters params;
+ try (GzipCompressorInputStream gzis = new GzipCompressorInputStream(
+ new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE))) {
+ params = gzis.getMetaData();
+ }
+
+ // Read trailer for stored CRC and size
+ byte[] fileBytes = Files.readAllBytes(inputFile.toPath());
+ if (fileBytes.length < 10) {
+ System.err.println("Error: File too small to be a valid GZIP file.");
+ System.exit(1);
+ }
+
+ int trailerStart = fileBytes.length - 8;
+ long storedCrc = ((long) (fileBytes[trailerStart] & 0xFF))
+ | ((long) (fileBytes[trailerStart + 1] & 0xFF) << 8)
+ | ((long) (fileBytes[trailerStart + 2] & 0xFF) << 16)
+ | ((long) (fileBytes[trailerStart + 3] & 0xFF) << 24);
+ long storedSize = ((long) (fileBytes[trailerStart + 4] & 0xFF))
+ | ((long) (fileBytes[trailerStart + 5] & 0xFF) << 8)
+ | ((long) (fileBytes[trailerStart + 6] & 0xFF) << 16)
+ | ((long) (fileBytes[trailerStart + 7] & 0xFF) << 24);
+
+ if (json) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("file", inputPath);
+ result.put("compressed_size", compressedSize);
+ result.put("original_size_mod32", storedSize);
+ result.put("compression_method", "DEFLATE");
+ result.put("crc32", String.format("0x%08X", storedCrc));
+ if (params.getFilename() != null) {
+ result.put("original_filename", params.getFilename());
+ }
+ if (compressedSize > 0 && storedSize > 0) {
+ result.put("ratio", (1.0 - (double) compressedSize / storedSize) * 100.0);
+ }
+ System.out.println(GSON.toJson(result));
+ } else {
+ System.out.println("File: " + inputPath);
+ System.out.println("Compressed size: " + formatSize(compressedSize));
+ System.out.println("Original size: " + formatSize(storedSize) + " (mod 2^32)");
+ System.out.println("Compression method: DEFLATE");
+ System.out.printf("CRC-32: 0x%08X%n", storedCrc);
+ if (params.getFilename() != null) {
+ System.out.println("Original filename: " + params.getFilename());
+ }
+ if (compressedSize > 0 && storedSize > 0) {
+ double ratio = (1.0 - (double) compressedSize / storedSize) * 100.0;
+ System.out.printf("Compression ratio: %.1f%%%n", ratio);
+ }
+ }
+ }
+
+ private static String formatSize(long sizeBytes) {
+ String[] units = {"B", "KB", "MB", "GB", "TB"};
+ double size = sizeBytes;
+ for (String unit : units) {
+ if (size < 1024.0) {
+ return String.format("%.2f %s", size, unit);
+ }
+ size /= 1024.0;
+ }
+ return String.format("%.2f PB", size);
+ }
+
+ private static String computeSha256(File file) throws IOException, NoSuchAlgorithmException {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ try (InputStream fis = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE)) {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int len;
+ while ((len = fis.read(buffer)) != -1) {
+ digest.update(buffer, 0, len);
+ }
+ }
+ byte[] hash = digest.digest();
+ StringBuilder sb = new StringBuilder();
+ for (byte b : hash) {
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
+ }
+}
03 / TASK AND FAILURE
claude-code/java-t2 #37 · read the task the agent was given
Claude Code wrote this java project from the task below. It does not run on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: GZIP Compression Tool Write a program that compresses and decompresses files using the GZIP format with configurable compression levels, integrity verification, and streaming support for large files. FUNCTIONAL REQUIREMENTS: - Accept a file path and mode (compress or decompress) as command-line arguments - Compress files using the DEFLATE algorithm wrapped in GZIP format (.gz extension) - Support configurable compression level via --level flag (1=fastest, 9=best compression; default: 6) - Decompress .gz files back to their original content, preserving the original filename stored in the GZIP header - Process files in streaming chunks to handle files larger than available memory, with configurable buffer size via --buffer flag (default: 64KB) - Display real-time progress during compression/decompression: percentage complete, current speed, and estimated time remaining - Verify file integrity after compression by checking CRC-32 checksums stored in the GZIP header via --verify flag - Support compressing multiple files via --batch flag (each file becomes a separate .gz file) - Preserve original file timestamps in the GZIP header and restore them during decompression - Support keeping or removing the original file via --keep flag (default: keep original) - Print compression summary: original size, compressed size, compression ratio, time taken, and throughput (MB/s) - Save operation log as JSON with --log flag - If no input is given, generate sample files of different types (text, CSV, JSON, binary with random data), compress each, compare compression ratios across file types, decompress, and verify integrity matches - Handle errors: corrupted GZIP files, truncated files, CRC mismatch, disk full, and unsupported GZIP extensions Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include: - Source code - pom.xml 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 checked by running the task · needs human review
misspecificationLabel rules and the text that matched
[
{
"category": "misspecification",
"rule": "signature.build_layout_mismatch",
"source": "failure_signature",
"excerpt": "error: no classes were compiled"
}
]Written by Claude Code (study run M1T2P37L2). It failed as written and was repaired by changing only its environment.
Commands install and build the declared environment as the study's tracing scripts did, then run the program with the command the study traced.
Preparation dates registries as the oracle does: Historical registry availability is not enforced for Maven/C++ system packages. Maven updatePolicy controls refresh frequency, not publication date.
05 / FILES
The project as the agent wrote it
2 files, exactly as written, before any repair.
GzipTool.java
package com.gziptool;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipParameters;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.zip.CRC32;
/**
* GZIP Compression Tool
*
* A command-line utility for compressing and decompressing files using GZIP/DEFLATE
* with streaming support and integrity verification.
*
* Dependencies:
* - Apache Commons Compress (1.25.0): Advanced compression/decompression library.
* - Gson (2.10.1): JSON serialization for structured output.
*/
public class GzipTool {
private static final int BUFFER_SIZE = 65536; // 64 KB
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static void main(String[] args) {
if (args.length < 1) {
printUsage();
System.exit(1);
}
String command = args[0].toLowerCase();
try {
switch (command) {
case "compress":
handleCompress(args);
break;
case "decompress":
handleDecompress(args);
break;
case "verify":
handleVerify(args);
break;
case "info":
handleInfo(args);
break;
case "--help":
case "-h":
printUsage();
break;
default:
System.err.println("Unknown command: " + command);
printUsage();
System.exit(1);
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
System.exit(1);
}
}
private static void printUsage() {
System.out.println("GZIP Compression Tool v1.0.0");
System.out.println();
System.out.println("Usage: java GzipTool <command> [options]");
System.out.println();
System.out.println("Commands:");
System.out.println(" compress <input> [-o output] [-l level] [-f] [-v] [--json]");
System.out.println(" decompress <input> [-o output] [-f] [-v]");
System.out.println(" verify <input> [-v] [--json]");
System.out.println(" info <input> [--json]");
}
private static String parseOption(String[] args, String shortOpt, String longOpt, String defaultVal) {
for (int i = 0; i < args.length - 1; i++) {
if (args[i].equals(shortOpt) || args[i].equals(longOpt)) {
return args[i + 1];
}
}
return defaultVal;
}
private static boolean hasFlag(String[] args, String shortFlag, String longFlag) {
for (String arg : args) {
if (arg.equals(shortFlag) || arg.equals(longFlag)) {
return true;
}
}
return false;
}
private static void handleCompress(String[] args) throws IOException, NoSuchAlgorithmException {
if (args.length < 2) {
System.err.println("Usage: compress <input> [-o output] [-l level] [-f] [-v] [--json]");
System.exit(1);
}
String inputPath = args[1];
File inputFile = new File(inputPath);
if (!inputFile.exists()) {
System.err.println("Error: Input file not found: " + inputPath);
System.exit(1);
}
String outputPath = parseOption(args, "-o", "--output", inputPath + ".gz");
int level = Integer.parseInt(parseOption(args, "-l", "--level", "6"));
boolean force = hasFlag(args, "-f", "--force");
boolean verbose = hasFlag(args, "-v", "--verbose");
boolean json = hasFlag(args, "--json", "--json");
if (level < 1 || level > 9) {
System.err.println("Error: Compression level must be between 1 and 9.");
System.exit(1);
}
File outputFile = new File(outputPath);
if (outputFile.exists() && !force) {
System.err.println("Error: Output file '" + outputPath + "' already exists. Use -f to overwrite.");
System.exit(1);
}
long inputSize = inputFile.length();
if (verbose) {
System.out.println("Input: " + inputPath + " (" + formatSize(inputSize) + ")");
System.out.println("Output: " + outputPath);
System.out.println("Compression level: " + level);
}
long startTime = System.currentTimeMillis();
GzipParameters params = new GzipParameters();
params.setCompressionLevel(level);
params.setFilename(inputFile.getName());
try (InputStream fis = new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE);
GzipCompressorOutputStream gzos = new GzipCompressorOutputStream(
new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER_SIZE), params)) {
byte[] buffer = new byte[BUFFER_SIZE];
long bytesRead = 0;
int len;
while ((len = fis.read(buffer)) != -1) {
gzos.write(buffer, 0, len);
bytesRead += len;
if (verbose && inputSize > 0) {
double progress = (double) bytesRead / inputSize * 100.0;
System.out.printf("\rProgress: %.1f%%", progress);
}
}
if (verbose) System.out.println();
}
long elapsed = System.currentTimeMillis() - startTime;
long outputSize = outputFile.length();
if (json) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("input", inputPath);
result.put("output", outputPath);
result.put("input_size", inputSize);
result.put("output_size", outputSize);
result.put("ratio", inputSize > 0 ? (1.0 - (double) outputSize / inputSize) * 100.0 : 0.0);
result.put("level", level);
result.put("elapsed_ms", elapsed);
result.put("sha256_input", computeSha256(inputFile));
result.put("sha256_output", computeSha256(outputFile));
System.out.println(GSON.toJson(result));
} else if (verbose) {
double ratio = inputSize > 0 ? (1.0 - (double) outputSize / inputSize) * 100.0 : 0;
System.out.println("Compressed size: " + formatSize(outputSize));
System.out.printf("Compression ratio: %.1f%%%n", ratio);
System.out.printf("Time: %.3fs%n", elapsed / 1000.0);
System.out.println("SHA-256 (input): " + computeSha256(inputFile));
System.out.println("SHA-256 (output): " + computeSha256(outputFile));
} else {
System.out.printf("Compressed '%s' -> '%s' (%s -> %s)%n",
inputPath, outputPath, formatSize(inputSize), formatSize(outputSize));
}
}
private static void handleDecompress(String[] args) throws IOException, NoSuchAlgorithmException {
if (args.length < 2) {
System.err.println("Usage: decompress <input> [-o output] [-f] [-v]");
System.exit(1);
}
String inputPath = args[1];
File inputFile = new File(inputPath);
if (!inputFile.exists()) {
System.err.println("Error: Input file not found: " + inputPath);
System.exit(1);
}
String defaultOutput = inputPath.endsWith(".gz")
? inputPath.substring(0, inputPath.length() - 3)
: inputPath + ".out";
String outputPath = parseOption(args, "-o", "--output", defaultOutput);
boolean force = hasFlag(args, "-f", "--force");
boolean verbose = hasFlag(args, "-v", "--verbose");
File outputFile = new File(outputPath);
if (outputFile.exists() && !force) {
System.err.println("Error: Output file '" + outputPath + "' already exists. Use -f to overwrite.");
System.exit(1);
}
long inputSize = inputFile.length();
if (verbose) {
System.out.println("Input: " + inputPath + " (" + formatSize(inputSize) + ")");
System.out.println("Output: " + outputPath);
}
long startTime = System.currentTimeMillis();
long bytesWritten = 0;
try (GzipCompressorInputStream gzis = new GzipCompressorInputStream(
new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE));
OutputStream fos = new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER_SIZE)) {
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = gzis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
bytesWritten += len;
if (verbose) {
System.out.printf("\rDecompressed: %s", formatSize(bytesWritten));
}
}
if (verbose) System.out.println();
}
long elapsed = System.currentTimeMillis() - startTime;
long outputSize = outputFile.length();
if (verbose) {
System.out.println("Decompressed size: " + formatSize(outputSize));
System.out.printf("Time: %.3fs%n", elapsed / 1000.0);
System.out.println("SHA-256 (output): " + computeSha256(outputFile));
} else {
System.out.printf("Decompressed '%s' -> '%s' (%s -> %s)%n",
inputPath, outputPath, formatSize(inputSize), formatSize(outputSize));
}
}
private static void handleVerify(String[] args) throws IOException {
if (args.length < 2) {
System.err.println("Usage: verify <input> [-v] [--json]");
System.exit(1);
}
String inputPath = args[1];
File inputFile = new File(inputPath);
boolean verbose = hasFlag(args, "-v", "--verbose");
boolean json = hasFlag(args, "--json", "--json");
if (!inputFile.exists()) {
System.err.println("Error: File not found: " + inputPath);
System.exit(1);
}
if (verbose) {
System.out.println("Verifying: " + inputPath);
}
// Check magic bytes
byte[] header = new byte[2];
try (InputStream is = new FileInputStream(inputFile)) {
if (is.read(header) < 2 || header[0] != (byte) 0x1f || header[1] != (byte) 0x8b) {
System.err.println("FAILED: Invalid GZIP magic bytes");
System.exit(1);
}
}
CRC32 crc = new CRC32();
long totalSize = 0;
try (GzipCompressorInputStream gzis = new GzipCompressorInputStream(
new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE))) {
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = gzis.read(buffer)) != -1) {
crc.update(buffer, 0, len);
totalSize += len;
}
} catch (IOException e) {
System.err.println("FAILED: " + e.getMessage());
System.exit(1);
}
long compressedSize = inputFile.length();
if (json) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("file", inputPath);
result.put("valid", true);
result.put("compressed_size", compressedSize);
result.put("original_size", totalSize);
result.put("crc32", String.format("0x%08X", crc.getValue()));
System.out.println(GSON.toJson(result));
} else if (verbose) {
System.out.println("Compressed size: " + formatSize(compressedSize));
System.out.println("Original size: " + formatSize(totalSize));
System.out.printf("CRC-32: 0x%08X%n", crc.getValue());
System.out.println("CRC-32 match: Yes");
System.out.println("Size match: Yes");
}
if (!json) {
System.out.printf("OK: '%s' integrity verified successfully.%n", inputPath);
}
}
private static void handleInfo(String[] args) throws IOException {
if (args.length < 2) {
System.err.println("Usage: info <input> [--json]");
System.exit(1);
}
String inputPath = args[1];
File inputFile = new File(inputPath);
boolean json = hasFlag(args, "--json", "--json");
if (!inputFile.exists()) {
System.err.println("Error: File not found: " + inputPath);
System.exit(1);
}
long compressedSize = inputFile.length();
// Read header using Commons Compress
GzipParameters params;
try (GzipCompressorInputStream gzis = new GzipCompressorInputStream(
new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE))) {
params = gzis.getMetaData();
}
// Read trailer for stored CRC and size
byte[] fileBytes = Files.readAllBytes(inputFile.toPath());
if (fileBytes.length < 10) {
System.err.println("Error: File too small to be a valid GZIP file.");
System.exit(1);
}
int trailerStart = fileBytes.length - 8;
long storedCrc = ((long) (fileBytes[trailerStart] & 0xFF))
| ((long) (fileBytes[trailerStart + 1] & 0xFF) << 8)
| ((long) (fileBytes[trailerStart + 2] & 0xFF) << 16)
| ((long) (fileBytes[trailerStart + 3] & 0xFF) << 24);
long storedSize = ((long) (fileBytes[trailerStart + 4] & 0xFF))
| ((long) (fileBytes[trailerStart + 5] & 0xFF) << 8)
| ((long) (fileBytes[trailerStart + 6] & 0xFF) << 16)
| ((long) (fileBytes[trailerStart + 7] & 0xFF) << 24);
if (json) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("file", inputPath);
result.put("compressed_size", compressedSize);
result.put("original_size_mod32", storedSize);
result.put("compression_method", "DEFLATE");
result.put("crc32", String.format("0x%08X", storedCrc));
if (params.getFilename() != null) {
result.put("original_filename", params.getFilename());
}
if (compressedSize > 0 && storedSize > 0) {
result.put("ratio", (1.0 - (double) compressedSize / storedSize) * 100.0);
}
System.out.println(GSON.toJson(result));
} else {
System.out.println("File: " + inputPath);
System.out.println("Compressed size: " + formatSize(compressedSize));
System.out.println("Original size: " + formatSize(storedSize) + " (mod 2^32)");
System.out.println("Compression method: DEFLATE");
System.out.printf("CRC-32: 0x%08X%n", storedCrc);
if (params.getFilename() != null) {
System.out.println("Original filename: " + params.getFilename());
}
if (compressedSize > 0 && storedSize > 0) {
double ratio = (1.0 - (double) compressedSize / storedSize) * 100.0;
System.out.printf("Compression ratio: %.1f%%%n", ratio);
}
}
}
private static String formatSize(long sizeBytes) {
String[] units = {"B", "KB", "MB", "GB", "TB"};
double size = sizeBytes;
for (String unit : units) {
if (size < 1024.0) {
return String.format("%.2f %s", size, unit);
}
size /= 1024.0;
}
return String.format("%.2f PB", size);
}
private static String computeSha256(File file) throws IOException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (InputStream fis = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE)) {
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = fis.read(buffer)) != -1) {
digest.update(buffer, 0, len);
}
}
byte[] hash = digest.digest();
StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gziptool</groupId>
<artifactId>gzip-compression-tool</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>GZIP Compression Tool</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Apache Commons Compress for advanced GZIP/DEFLATE compression -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.25.0</version>
</dependency>
<!-- Gson for JSON-formatted output -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.gziptool.GzipTool</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>gzip-tool</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>