From e33aa13edc29105ce462dcaedb9f0e884b77b957 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Wed, 29 Apr 2026 19:58:46 +0200 Subject: [PATCH] feat(Compress::Bzip2): real bzip2 backend via Apache Commons Compress Two issues kept `./jcpan -t CPAN::Checksums` from running: 1. Shebang parser rejected emacs mode markers like `#!/usr/bin/perl -w -*- mode: cperl -*-` with `Unrecognized switch: -*-`. Real perl tolerates `-*- ... -*-` in the shebang line. ArgumentParser now strips the marker before processing switches. 2. CPAN::Checksums does `use Compress::Bzip2();` unconditionally, but PerlOnJava had no Bzip2 implementation, so module load died in XSLoader. Add a real Java-backed Compress::Bzip2: - Add commons-compress 1.27.1 dependency (Gradle, Maven, version catalog). - CompressBzip2 / CompressBzip2BzFile Java modules implementing memBzip, memBunzip, bzip2, bzunzip, bzopen, and the OO handle methods bzread, bzwrite, bzreadline, bzeof, bzclose, bzerror, plus the BZ_* status constants. Mirrors the existing CompressZlib / CompressZlibGzFile pattern. - lib/Compress/Bzip2.pm calls XSLoader::load('Compress::Bzip2'), which auto-discovers the Java backend (no GlobalContext wiring needed). Verified: in-memory and file round-trips work; `jcpan -t CPAN::Checksums` now passes (Files=4, Tests=31, Result: PASS); `make` (full unit test suite) is green. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- build.gradle | 1 + gradle/libs.versions.toml | 2 + pom.xml | 5 + .../perlonjava/app/cli/ArgumentParser.java | 14 +- .../org/perlonjava/core/Configuration.java | 4 +- .../runtime/perlmodule/CompressBzip2.java | 202 ++++++++++++++++++ .../perlmodule/CompressBzip2BzFile.java | 188 ++++++++++++++++ src/main/perl/lib/Compress/Bzip2.pm | 63 ++++++ 8 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2.java create mode 100644 src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2BzFile.java create mode 100644 src/main/perl/lib/Compress/Bzip2.pm diff --git a/build.gradle b/build.gradle index 9d3f7e504..b0fe4f232 100644 --- a/build.gradle +++ b/build.gradle @@ -204,6 +204,7 @@ dependencies { implementation libs.snakeyaml.engine // YAML processing implementation libs.tomlj // TOML processing implementation libs.commons.csv // CSV processing + implementation libs.commons.compress // BZip2/tar/etc. compressors implementation libs.sqlite.jdbc // SQLite JDBC driver implementation libs.bcprov // Bouncy Castle crypto (SHA-3, Keccak, etc.) implementation libs.bcpkix // Bouncy Castle PEM/PKCS parsing diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4e4c0a172..9156544f3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ asm = "9.9.1" bouncycastle = "1.78.1" commons-csv = "1.14.1" +commons-compress = "1.27.1" icu4j = "78.3" junit-jupiter = "6.1.0-M1" snakeyaml-engine = "3.0.1" @@ -14,6 +15,7 @@ asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } bcprov = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bouncycastle" } bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bouncycastle" } commons-csv = { module = "org.apache.commons:commons-csv", version.ref = "commons-csv" } +commons-compress = { module = "org.apache.commons:commons-compress", version.ref = "commons-compress" } icu4j = { module = "com.ibm.icu:icu4j", version.ref = "icu4j" } junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" } junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" } diff --git a/pom.xml b/pom.xml index 8b4be3d36..98c548385 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,11 @@ commons-csv 1.14.1 + + org.apache.commons + commons-compress + 1.27.1 + org.xerial sqlite-jdbc diff --git a/src/main/java/org/perlonjava/app/cli/ArgumentParser.java b/src/main/java/org/perlonjava/app/cli/ArgumentParser.java index 02754d7fb..57c529842 100644 --- a/src/main/java/org/perlonjava/app/cli/ArgumentParser.java +++ b/src/main/java/org/perlonjava/app/cli/ArgumentParser.java @@ -319,7 +319,19 @@ private static void processShebangLine(String[] args, CompilerOptions parsedArgs int perlIndex = shebangLine.indexOf("perl"); if (perlIndex != -1) { String relevantPart = shebangLine.substring(perlIndex + 4).trim(); - String[] shebangArgs = relevantPart.split("\\s+"); + // Strip emacs mode line marker (e.g. "-*- mode: cperl -*-") which real + // perl tolerates in #! lines but not on the command line. + int emacsStart = relevantPart.indexOf("-*-"); + if (emacsStart != -1) { + int emacsEnd = relevantPart.indexOf("-*-", emacsStart + 3); + if (emacsEnd != -1) { + relevantPart = relevantPart.substring(0, emacsStart) + + relevantPart.substring(emacsEnd + 3); + } else { + relevantPart = relevantPart.substring(0, emacsStart); + } + } + String[] shebangArgs = relevantPart.trim().split("\\s+"); // Filter out empty args from shebang processing String[] nonEmptyArgs = Arrays.stream(shebangArgs) .filter(arg -> !arg.isEmpty()) diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 61fa880eb..45d16a907 100644 --- a/src/main/java/org/perlonjava/core/Configuration.java +++ b/src/main/java/org/perlonjava/core/Configuration.java @@ -33,7 +33,7 @@ public final class Configuration { * Automatically populated by Gradle/Maven during build. * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String gitCommitId = "5f8a9d5e5"; + public static final String gitCommitId = "ccf4d54b3"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). @@ -48,7 +48,7 @@ public final class Configuration { * Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at" * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String buildTimestamp = "Apr 29 2026 19:52:04"; + public static final String buildTimestamp = "Apr 29 2026 19:59:08"; // Prevent instantiation private Configuration() { diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2.java b/src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2.java new file mode 100644 index 000000000..ddda7ee8f --- /dev/null +++ b/src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2.java @@ -0,0 +1,202 @@ +package org.perlonjava.runtime.perlmodule; + +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; +import org.perlonjava.runtime.operators.ReferenceOperators; +import org.perlonjava.runtime.runtimetypes.*; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +import static org.perlonjava.runtime.runtimetypes.RuntimeScalarCache.scalarUndef; + +/** + * Implements the Compress::Bzip2 module on top of Apache Commons Compress. + * + *

The upstream CPAN module (Rob Janes) is XS-only. This Java backend + * provides the API surface that pure-Perl callers use: + *

+ * Companion class {@link CompressBzip2BzFile} provides the bzread / bzwrite / + * bzclose / bzreadline / bzeof methods on the returned handle objects. + */ +public class CompressBzip2 extends PerlModuleBase { + + public CompressBzip2() { + super("Compress::Bzip2", false); + } + + public static void initialize() { + CompressBzip2 cb = new CompressBzip2(); + try { + cb.registerMethod("memBzip", null); + cb.registerMethod("memBunzip", null); + cb.registerMethod("bzip2", null); + cb.registerMethod("bzunzip", null); + cb.registerMethod("bzopen", null); + + // libbzip2 status / mode constants. The numeric values mirror the + // upstream Compress::Bzip2 / libbzip2 headers so callers comparing + // against e.g. BZ_STREAM_END keep working. + cb.registerMethod("BZ_OK", null); + cb.registerMethod("BZ_RUN_OK", null); + cb.registerMethod("BZ_FLUSH_OK", null); + cb.registerMethod("BZ_FINISH_OK", null); + cb.registerMethod("BZ_STREAM_END", null); + cb.registerMethod("BZ_SEQUENCE_ERROR", null); + cb.registerMethod("BZ_PARAM_ERROR", null); + cb.registerMethod("BZ_MEM_ERROR", null); + cb.registerMethod("BZ_DATA_ERROR", null); + cb.registerMethod("BZ_DATA_ERROR_MAGIC", null); + cb.registerMethod("BZ_IO_ERROR", null); + cb.registerMethod("BZ_UNEXPECTED_EOF", null); + cb.registerMethod("BZ_OUTBUFF_FULL", null); + cb.registerMethod("BZ_CONFIG_ERROR", null); + cb.registerMethod("BZ_RUN", null); + cb.registerMethod("BZ_FLUSH", null); + cb.registerMethod("BZ_FINISH", null); + cb.registerMethod("BZ_MAX_UNUSED", null); + } catch (NoSuchMethodException e) { + System.err.println("Warning: Missing Compress::Bzip2 method: " + e.getMessage()); + } + + CompressBzip2BzFile.initialize(); + } + + public static RuntimeList BZ_OK(RuntimeArray args, int ctx) { return new RuntimeScalar(0).getList(); } + public static RuntimeList BZ_RUN_OK(RuntimeArray args, int ctx) { return new RuntimeScalar(1).getList(); } + public static RuntimeList BZ_FLUSH_OK(RuntimeArray args, int ctx) { return new RuntimeScalar(2).getList(); } + public static RuntimeList BZ_FINISH_OK(RuntimeArray args, int ctx) { return new RuntimeScalar(3).getList(); } + public static RuntimeList BZ_STREAM_END(RuntimeArray args, int ctx) { return new RuntimeScalar(4).getList(); } + public static RuntimeList BZ_SEQUENCE_ERROR(RuntimeArray args, int ctx) { return new RuntimeScalar(-1).getList(); } + public static RuntimeList BZ_PARAM_ERROR(RuntimeArray args, int ctx) { return new RuntimeScalar(-2).getList(); } + public static RuntimeList BZ_MEM_ERROR(RuntimeArray args, int ctx) { return new RuntimeScalar(-3).getList(); } + public static RuntimeList BZ_DATA_ERROR(RuntimeArray args, int ctx) { return new RuntimeScalar(-4).getList(); } + public static RuntimeList BZ_DATA_ERROR_MAGIC(RuntimeArray args, int ctx) { return new RuntimeScalar(-5).getList(); } + public static RuntimeList BZ_IO_ERROR(RuntimeArray args, int ctx) { return new RuntimeScalar(-6).getList(); } + public static RuntimeList BZ_UNEXPECTED_EOF(RuntimeArray args, int ctx) { return new RuntimeScalar(-7).getList(); } + public static RuntimeList BZ_OUTBUFF_FULL(RuntimeArray args, int ctx) { return new RuntimeScalar(-8).getList(); } + public static RuntimeList BZ_CONFIG_ERROR(RuntimeArray args, int ctx) { return new RuntimeScalar(-9).getList(); } + public static RuntimeList BZ_RUN(RuntimeArray args, int ctx) { return new RuntimeScalar(0).getList(); } + public static RuntimeList BZ_FLUSH(RuntimeArray args, int ctx) { return new RuntimeScalar(1).getList(); } + public static RuntimeList BZ_FINISH(RuntimeArray args, int ctx) { return new RuntimeScalar(2).getList(); } + public static RuntimeList BZ_MAX_UNUSED(RuntimeArray args, int ctx) { return new RuntimeScalar(5000).getList(); } + + /** + * Treat a scalar (or scalar reference) as a byte string. Mirrors the + * helper in {@link CompressZlib}. + */ + private static byte[] getInputBytes(RuntimeScalar dataScalar) { + RuntimeScalar actual = dataScalar; + if (dataScalar.type == RuntimeScalarType.REFERENCE) { + actual = dataScalar.scalarDeref(); + } + return actual.toString().getBytes(StandardCharsets.ISO_8859_1); + } + + private static RuntimeScalar bytesToScalar(byte[] bytes, int len) { + RuntimeScalar s = new RuntimeScalar(new String(bytes, 0, len, StandardCharsets.ISO_8859_1)); + s.type = RuntimeScalarType.BYTE_STRING; + return s; + } + + /** + * memBzip($data) — one-shot compression. Returns the bzip2 stream or + * undef on error (matching the upstream module). + */ + public static RuntimeList memBzip(RuntimeArray args, int ctx) { + if (args.isEmpty()) return scalarUndef.getList(); + byte[] input = getInputBytes(args.get(0)); + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(Math.max(64, input.length / 2)); + try (BZip2CompressorOutputStream out = new BZip2CompressorOutputStream(baos)) { + out.write(input); + } + byte[] result = baos.toByteArray(); + return bytesToScalar(result, result.length).getList(); + } catch (IOException e) { + return scalarUndef.getList(); + } + } + + /** + * memBunzip($data) — one-shot decompression. Returns the inflated bytes + * or undef on error. + */ + public static RuntimeList memBunzip(RuntimeArray args, int ctx) { + if (args.isEmpty()) return scalarUndef.getList(); + byte[] input = getInputBytes(args.get(0)); + try (BZip2CompressorInputStream in = new BZip2CompressorInputStream( + new ByteArrayInputStream(input), true)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length * 4); + byte[] buf = new byte[4096]; + int n; + while ((n = in.read(buf)) != -1) { + baos.write(buf, 0, n); + } + byte[] result = baos.toByteArray(); + return bytesToScalar(result, result.length).getList(); + } catch (IOException e) { + return scalarUndef.getList(); + } + } + + /** bzip2($data) — alias of memBzip in the upstream module. */ + public static RuntimeList bzip2(RuntimeArray args, int ctx) { + return memBzip(args, ctx); + } + + /** bzunzip($data) — alias of memBunzip. */ + public static RuntimeList bzunzip(RuntimeArray args, int ctx) { + return memBunzip(args, ctx); + } + + /** + * bzopen($filename, $mode) — opens a bzip2 file for reading ('rb') or + * writing ('wb'). Returns a blessed Compress::Bzip2::bzFile handle, or + * undef on error. Method-call syntax (Compress::Bzip2->bzopen) is + * also accepted. + */ + public static RuntimeList bzopen(RuntimeArray args, int ctx) { + int argOffset = 0; + if (!args.isEmpty()) { + String first = args.get(0).toString(); + if (first.equals("Compress::Bzip2") || first.contains("::")) { + argOffset = 1; + } + } + if (args.size() < argOffset + 2) return scalarUndef.getList(); + + String filename = args.get(argOffset).toString(); + String mode = args.get(argOffset + 1).toString(); + + try { + RuntimeHash self = new RuntimeHash(); + self.put("_mode", new RuntimeScalar(mode)); + self.put("_eof", new RuntimeScalar(0)); + + if (mode.startsWith("r")) { + // Concatenated streams (true) so multi-block .bz2 archives + // (typical for tarballs) are read end-to-end. + InputStream fis = new FileInputStream(filename); + BZip2CompressorInputStream in = new BZip2CompressorInputStream(fis, true); + self.put("_stream", new RuntimeScalar(in)); + } else if (mode.startsWith("w")) { + OutputStream fos = new FileOutputStream(filename); + BZip2CompressorOutputStream out = new BZip2CompressorOutputStream(fos); + self.put("_stream", new RuntimeScalar(out)); + } else { + return scalarUndef.getList(); + } + + RuntimeScalar ref = self.createReference(); + ReferenceOperators.bless(ref, new RuntimeScalar("Compress::Bzip2::bzFile")); + return ref.getList(); + } catch (IOException e) { + return scalarUndef.getList(); + } + } +} diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2BzFile.java b/src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2BzFile.java new file mode 100644 index 000000000..f14528e8c --- /dev/null +++ b/src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2BzFile.java @@ -0,0 +1,188 @@ +package org.perlonjava.runtime.perlmodule; + +import org.perlonjava.runtime.runtimetypes.*; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +import static org.perlonjava.runtime.runtimetypes.RuntimeScalarCache.scalarUndef; + +/** + * Implements the Compress::Bzip2::bzFile class returned by + * {@link CompressBzip2#bzopen}. Provides bzread / bzwrite / bzreadline / + * bzeof / bzclose / bzerror methods, matching the OO API of the upstream + * Compress::Bzip2 CPAN module. + * + *

Mirrors the pattern of {@link CompressZlibGzFile} for consistency. + */ +public class CompressBzip2BzFile extends PerlModuleBase { + + private static final String STREAM_KEY = "_stream"; + private static final String EOF_KEY = "_eof"; + + public CompressBzip2BzFile() { + super("Compress::Bzip2::bzFile", false); + } + + public static void initialize() { + CompressBzip2BzFile bz = new CompressBzip2BzFile(); + try { + bz.registerMethod("bzread", null); + bz.registerMethod("bzwrite", null); + bz.registerMethod("bzreadline", null); + bz.registerMethod("bzeof", null); + bz.registerMethod("bzclose", null); + bz.registerMethod("bzerror", null); + } catch (NoSuchMethodException e) { + System.err.println("Warning: Missing Compress::Bzip2::bzFile method: " + e.getMessage()); + } + } + + /** + * $bz->bzread($buffer [, $size]) — fills $buffer in place and returns the + * number of bytes read (0 on EOF, -1 on error). + */ + public static RuntimeList bzread(RuntimeArray args, int ctx) { + if (args.size() < 2) return new RuntimeScalar(-1).getList(); + + RuntimeHash self = args.get(0).hashDeref(); + int nbytes = args.size() >= 3 ? args.get(2).getInt() : 4096; + + RuntimeScalar streamScalar = self.get(STREAM_KEY); + if (streamScalar == null || streamScalar.type != RuntimeScalarType.JAVAOBJECT + || !(streamScalar.value instanceof InputStream is)) { + return new RuntimeScalar(-1).getList(); + } + + try { + byte[] buf = new byte[nbytes]; + int totalRead = 0; + while (totalRead < nbytes) { + int n = is.read(buf, totalRead, nbytes - totalRead); + if (n == -1) { + self.put(EOF_KEY, new RuntimeScalar(1)); + break; + } + totalRead += n; + } + + if (totalRead == 0) { + args.get(1).set(""); + return new RuntimeScalar(0).getList(); + } + + String data = new String(buf, 0, totalRead, StandardCharsets.ISO_8859_1); + args.get(1).set(data); + return new RuntimeScalar(totalRead).getList(); + } catch (IOException e) { + return new RuntimeScalar(-1).getList(); + } + } + + /** $bz->bzwrite($data) — returns bytes written or 0 on error. */ + public static RuntimeList bzwrite(RuntimeArray args, int ctx) { + if (args.size() < 2) return new RuntimeScalar(0).getList(); + + RuntimeHash self = args.get(0).hashDeref(); + String data = args.get(1).toString(); + + RuntimeScalar streamScalar = self.get(STREAM_KEY); + if (streamScalar == null || streamScalar.type != RuntimeScalarType.JAVAOBJECT + || !(streamScalar.value instanceof OutputStream os)) { + return new RuntimeScalar(0).getList(); + } + + try { + byte[] bytes = data.getBytes(StandardCharsets.ISO_8859_1); + os.write(bytes); + return new RuntimeScalar(bytes.length).getList(); + } catch (IOException e) { + return new RuntimeScalar(0).getList(); + } + } + + /** + * $bz->bzreadline($line) — reads one line into $line in place, returns + * length read (0 on EOF, -1 on error). + */ + public static RuntimeList bzreadline(RuntimeArray args, int ctx) { + if (args.size() < 2) return new RuntimeScalar(-1).getList(); + + RuntimeHash self = args.get(0).hashDeref(); + + RuntimeScalar streamScalar = self.get(STREAM_KEY); + if (streamScalar == null || streamScalar.type != RuntimeScalarType.JAVAOBJECT + || !(streamScalar.value instanceof InputStream is)) { + return new RuntimeScalar(-1).getList(); + } + + try { + StringBuilder line = new StringBuilder(); + int c; + while ((c = is.read()) != -1) { + line.append((char) c); + if (c == '\n') break; + } + + if (line.isEmpty()) { + self.put(EOF_KEY, new RuntimeScalar(1)); + args.get(1).set(""); + return new RuntimeScalar(0).getList(); + } + + String result = line.toString(); + args.get(1).set(result); + return new RuntimeScalar(result.length()).getList(); + } catch (IOException e) { + return new RuntimeScalar(-1).getList(); + } + } + + /** $bz->bzeof — 1 if at end of stream, 0 otherwise. */ + public static RuntimeList bzeof(RuntimeArray args, int ctx) { + if (args.isEmpty()) return new RuntimeScalar(0).getList(); + RuntimeHash self = args.get(0).hashDeref(); + RuntimeScalar eofScalar = self.get(EOF_KEY); + return new RuntimeScalar(eofScalar != null ? eofScalar.getInt() : 0).getList(); + } + + /** $bz->bzclose — closes the stream. Returns 0 (BZ_OK) on success, -1 on error. */ + public static RuntimeList bzclose(RuntimeArray args, int ctx) { + if (args.isEmpty()) return new RuntimeScalar(-1).getList(); + + RuntimeHash self = args.get(0).hashDeref(); + RuntimeScalar streamScalar = self.get(STREAM_KEY); + if (streamScalar == null || streamScalar.type != RuntimeScalarType.JAVAOBJECT) { + return new RuntimeScalar(-1).getList(); + } + + try { + Object stream = streamScalar.value; + if (stream instanceof OutputStream os) { + os.flush(); + os.close(); + } else if (stream instanceof InputStream is) { + is.close(); + } + self.put(STREAM_KEY, scalarUndef); + self.put(EOF_KEY, new RuntimeScalar(1)); + return new RuntimeScalar(0).getList(); + } catch (IOException e) { + return new RuntimeScalar(-1).getList(); + } + } + + /** + * $bz->bzerror — no error tracking; always reports success. Returns + * "" in scalar context, ("", 0) in list context (matching upstream). + */ + public static RuntimeList bzerror(RuntimeArray args, int ctx) { + if (ctx == RuntimeContextType.LIST) { + RuntimeList result = new RuntimeList(); + result.add(new RuntimeScalar("")); + result.add(new RuntimeScalar(0)); + return result; + } + return new RuntimeScalar("").getList(); + } +} diff --git a/src/main/perl/lib/Compress/Bzip2.pm b/src/main/perl/lib/Compress/Bzip2.pm new file mode 100644 index 000000000..69b796d81 --- /dev/null +++ b/src/main/perl/lib/Compress/Bzip2.pm @@ -0,0 +1,63 @@ +package Compress::Bzip2; + +# +# Original Compress::Bzip2 module by Rob Janes. +# Copyright (c) 2005 Rob Janes. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# PerlOnJava implementation by Flavio S. Glock. +# The implementation is in: +# src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2.java +# src/main/java/org/perlonjava/runtime/perlmodule/CompressBzip2BzFile.java +# It is backed by Apache Commons Compress (BZip2CompressorInputStream / +# BZip2CompressorOutputStream). +# + +use strict; +use warnings; + +our $VERSION = '2.28'; + +require Exporter; +our @ISA = qw(Exporter); + +XSLoader::load('Compress::Bzip2'); + +our %EXPORT_TAGS = ( + 'constants' => [ qw( + BZ_CONFIG_ERROR BZ_DATA_ERROR BZ_DATA_ERROR_MAGIC + BZ_FINISH BZ_FINISH_OK BZ_FLUSH BZ_FLUSH_OK + BZ_IO_ERROR BZ_MAX_UNUSED BZ_MEM_ERROR + BZ_OK BZ_OUTBUFF_FULL BZ_PARAM_ERROR + BZ_RUN BZ_RUN_OK BZ_SEQUENCE_ERROR + BZ_STREAM_END BZ_UNEXPECTED_EOF + ) ], + 'utilities' => [ qw( + memBzip memBunzip bzip2 bzunzip + ) ], + 'bzip1' => [ qw(bzopen bzclose bzread bzreadline bzwrite bzeof bzerror) ], + 'gzip' => [ qw(bzopen bzclose bzread bzreadline bzwrite bzeof bzerror) ], +); +our @EXPORT_OK = ( map { @$_ } values %EXPORT_TAGS ); +$EXPORT_TAGS{'all'} = [ @EXPORT_OK ]; +our @EXPORT = qw(); + +1; + +__END__ + +=head1 NAME + +Compress::Bzip2 - PerlOnJava implementation of Compress::Bzip2 + +=head1 DESCRIPTION + +Provides bzip2 compression and decompression backed by the Apache Commons +Compress Java library. The Perl-visible API matches the upstream +L CPAN module: one-shot helpers (C, C, +C, C) and a file-handle interface returned by C +with C, C, C, C, C, and +C. + +=cut