omnizip 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Rar
|
|
6
|
+
# RAR format constants
|
|
7
|
+
module Constants
|
|
8
|
+
# RAR signatures
|
|
9
|
+
RAR4_SIGNATURE = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00].freeze
|
|
10
|
+
RAR5_SIGNATURE = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00].freeze
|
|
11
|
+
|
|
12
|
+
# Block types (RAR4)
|
|
13
|
+
BLOCK_MARKER = 0x72
|
|
14
|
+
BLOCK_ARCHIVE = 0x73
|
|
15
|
+
BLOCK_FILE = 0x74
|
|
16
|
+
BLOCK_COMMENT = 0x75
|
|
17
|
+
BLOCK_OLD_EXTRA = 0x76
|
|
18
|
+
BLOCK_OLD_SUBBLOCK = 0x77
|
|
19
|
+
BLOCK_OLD_RECOVERY = 0x78
|
|
20
|
+
BLOCK_OLD_AUTH = 0x79
|
|
21
|
+
BLOCK_SUBBLOCK = 0x7A
|
|
22
|
+
BLOCK_ENDARC = 0x7B
|
|
23
|
+
|
|
24
|
+
# Archive flags
|
|
25
|
+
ARCHIVE_VOLUME = 0x0001
|
|
26
|
+
ARCHIVE_COMMENT = 0x0002
|
|
27
|
+
ARCHIVE_LOCKED = 0x0004
|
|
28
|
+
ARCHIVE_SOLID = 0x0008
|
|
29
|
+
ARCHIVE_NEW_NAMING = 0x0010
|
|
30
|
+
ARCHIVE_AUTH_INFO = 0x0020
|
|
31
|
+
ARCHIVE_RECOVERY = 0x0040
|
|
32
|
+
ARCHIVE_ENCRYPTED = 0x0080
|
|
33
|
+
ARCHIVE_FIRST_VOLUME = 0x0100
|
|
34
|
+
|
|
35
|
+
# File flags
|
|
36
|
+
FILE_SPLIT_BEFORE = 0x0001
|
|
37
|
+
FILE_SPLIT_AFTER = 0x0002
|
|
38
|
+
FILE_ENCRYPTED = 0x0004
|
|
39
|
+
FILE_COMMENT = 0x0008
|
|
40
|
+
FILE_SOLID = 0x0010
|
|
41
|
+
FILE_DIRECTORY = 0x00E0
|
|
42
|
+
FILE_LARGE = 0x0100
|
|
43
|
+
FILE_UNICODE = 0x0200
|
|
44
|
+
FILE_SALT = 0x0400
|
|
45
|
+
FILE_VERSION = 0x0800
|
|
46
|
+
FILE_EXT_TIME = 0x1000
|
|
47
|
+
|
|
48
|
+
# RAR5 header types
|
|
49
|
+
RAR5_HEADER_MAIN = 1
|
|
50
|
+
RAR5_HEADER_FILE = 2
|
|
51
|
+
RAR5_HEADER_SERVICE = 3
|
|
52
|
+
RAR5_HEADER_ENCRYPTION = 4
|
|
53
|
+
RAR5_HEADER_END = 5
|
|
54
|
+
|
|
55
|
+
# RAR5 flags
|
|
56
|
+
RAR5_FLAG_EXTRA_AREA = 0x0001
|
|
57
|
+
RAR5_FLAG_DATA_AREA = 0x0002
|
|
58
|
+
RAR5_FLAG_UNKNOWN_BLOCKS = 0x0004
|
|
59
|
+
RAR5_FLAG_DATA_INHERITED = 0x0008
|
|
60
|
+
RAR5_FLAG_CHILD_BLOCKS = 0x0010
|
|
61
|
+
RAR5_FLAG_IS_DIR = 0x0020
|
|
62
|
+
RAR5_FLAG_MULTI_VOLUME = 0x0001
|
|
63
|
+
|
|
64
|
+
# Compression methods
|
|
65
|
+
METHOD_STORE = 0x30
|
|
66
|
+
METHOD_FASTEST = 0x31
|
|
67
|
+
METHOD_FAST = 0x32
|
|
68
|
+
METHOD_NORMAL = 0x33
|
|
69
|
+
METHOD_GOOD = 0x34
|
|
70
|
+
METHOD_BEST = 0x35
|
|
71
|
+
|
|
72
|
+
# Host OS
|
|
73
|
+
OS_MSDOS = 0
|
|
74
|
+
OS_OS2 = 1
|
|
75
|
+
OS_WIN32 = 2
|
|
76
|
+
OS_UNIX = 3
|
|
77
|
+
OS_MACOS = 4
|
|
78
|
+
OS_BEOS = 5
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
module Omnizip
|
|
5
|
+
module Formats
|
|
6
|
+
module Rar
|
|
7
|
+
# RAR decompressor wrapper
|
|
8
|
+
# Provides fallback chain: unrar gem → system command → error
|
|
9
|
+
class Decompressor
|
|
10
|
+
class << self
|
|
11
|
+
# Check if RAR decompression is available
|
|
12
|
+
#
|
|
13
|
+
# @return [Boolean] true if available
|
|
14
|
+
def available?
|
|
15
|
+
gem_available? || command_available?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Get decompressor information
|
|
19
|
+
#
|
|
20
|
+
# @return [Hash] Decompressor type and version
|
|
21
|
+
def info
|
|
22
|
+
if gem_available?
|
|
23
|
+
{ type: :gem, version: gem_version }
|
|
24
|
+
elsif command_available?
|
|
25
|
+
{ type: :command, version: command_version }
|
|
26
|
+
else
|
|
27
|
+
{ type: :none, version: nil }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Check if unrar gem is available
|
|
32
|
+
#
|
|
33
|
+
# @return [Boolean] true if gem available
|
|
34
|
+
def gem_available?
|
|
35
|
+
require "unrar"
|
|
36
|
+
true
|
|
37
|
+
rescue LoadError
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Check if system unrar command is available
|
|
42
|
+
#
|
|
43
|
+
# @return [Boolean] true if command available
|
|
44
|
+
def command_available?
|
|
45
|
+
!command_path.nil?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Get unrar command path
|
|
49
|
+
#
|
|
50
|
+
# @return [String, nil] Path to unrar or nil
|
|
51
|
+
def command_path
|
|
52
|
+
@command_path ||= find_command
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Extract RAR archive to directory
|
|
56
|
+
#
|
|
57
|
+
# @param archive_path [String] Path to RAR archive
|
|
58
|
+
# @param output_dir [String] Output directory
|
|
59
|
+
# @param password [String, nil] Optional password
|
|
60
|
+
# @raise [RuntimeError] if extraction fails
|
|
61
|
+
def extract(archive_path, output_dir, password: nil)
|
|
62
|
+
raise "RAR extraction not available" unless available?
|
|
63
|
+
|
|
64
|
+
if gem_available?
|
|
65
|
+
extract_with_gem(archive_path, output_dir, password)
|
|
66
|
+
elsif command_available?
|
|
67
|
+
extract_with_command(archive_path, output_dir, password)
|
|
68
|
+
else
|
|
69
|
+
raise unsupported_error
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# List RAR archive contents
|
|
74
|
+
#
|
|
75
|
+
# @param archive_path [String] Path to RAR archive
|
|
76
|
+
# @return [Array<Hash>] Entry information
|
|
77
|
+
# @raise [RuntimeError] if listing fails
|
|
78
|
+
def list(archive_path)
|
|
79
|
+
raise "RAR extraction not available" unless available?
|
|
80
|
+
|
|
81
|
+
if gem_available?
|
|
82
|
+
list_with_gem(archive_path)
|
|
83
|
+
elsif command_available?
|
|
84
|
+
list_with_command(archive_path)
|
|
85
|
+
else
|
|
86
|
+
raise unsupported_error
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Extract single entry from RAR
|
|
91
|
+
#
|
|
92
|
+
# @param archive_path [String] Path to RAR archive
|
|
93
|
+
# @param entry_name [String] Entry name
|
|
94
|
+
# @param output_path [String] Output path
|
|
95
|
+
# @param password [String, nil] Optional password
|
|
96
|
+
def extract_entry(archive_path, entry_name, output_path,
|
|
97
|
+
password: nil)
|
|
98
|
+
raise "RAR extraction not available" unless available?
|
|
99
|
+
|
|
100
|
+
if gem_available?
|
|
101
|
+
extract_entry_with_gem(archive_path, entry_name,
|
|
102
|
+
output_path, password)
|
|
103
|
+
elsif command_available?
|
|
104
|
+
extract_entry_with_command(archive_path, entry_name,
|
|
105
|
+
output_path, password)
|
|
106
|
+
else
|
|
107
|
+
raise unsupported_error
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# Find unrar command
|
|
114
|
+
#
|
|
115
|
+
# @return [String, nil] Command path or nil
|
|
116
|
+
def find_command
|
|
117
|
+
["unrar", "/usr/bin/unrar", "/usr/local/bin/unrar"].each do |cmd|
|
|
118
|
+
return cmd if system("which #{cmd} > /dev/null 2>&1")
|
|
119
|
+
end
|
|
120
|
+
nil
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Get gem version
|
|
124
|
+
#
|
|
125
|
+
# @return [String] Version string
|
|
126
|
+
def gem_version
|
|
127
|
+
require "unrar"
|
|
128
|
+
begin
|
|
129
|
+
Unrar::VERSION
|
|
130
|
+
rescue StandardError
|
|
131
|
+
"unknown"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Get command version
|
|
136
|
+
#
|
|
137
|
+
# @return [String] Version string
|
|
138
|
+
def command_version
|
|
139
|
+
return nil unless command_available?
|
|
140
|
+
|
|
141
|
+
output = `#{command_path} 2>&1`
|
|
142
|
+
output.match(/UNRAR\s+([\d.]+)/i)&.captures&.first || "unknown"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Extract with unrar gem
|
|
146
|
+
def extract_with_gem(archive_path, output_dir, password)
|
|
147
|
+
require "unrar"
|
|
148
|
+
Unrar.extract(archive_path, output_dir, password: password)
|
|
149
|
+
rescue StandardError => e
|
|
150
|
+
raise "Gem extraction failed: #{e.message}"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Extract with system command
|
|
154
|
+
def extract_with_command(archive_path, output_dir, password)
|
|
155
|
+
cmd = build_extract_command(archive_path, output_dir, password)
|
|
156
|
+
return if system(cmd)
|
|
157
|
+
|
|
158
|
+
raise "Command extraction failed: #{archive_path}"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# List with unrar gem
|
|
162
|
+
def list_with_gem(archive_path)
|
|
163
|
+
require "unrar"
|
|
164
|
+
archive = Unrar::Archive.new(archive_path)
|
|
165
|
+
archive.list.map do |entry|
|
|
166
|
+
{
|
|
167
|
+
name: entry.filename,
|
|
168
|
+
size: entry.unpacked_size,
|
|
169
|
+
compressed_size: entry.packed_size,
|
|
170
|
+
is_dir: entry.directory?,
|
|
171
|
+
mtime: entry.file_time,
|
|
172
|
+
}
|
|
173
|
+
end
|
|
174
|
+
rescue StandardError => e
|
|
175
|
+
raise "Gem listing failed: #{e.message}"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# List with system command
|
|
179
|
+
def list_with_command(archive_path)
|
|
180
|
+
output = `#{command_path} vb "#{archive_path}" 2>&1`
|
|
181
|
+
raise "Command listing failed" unless $CHILD_STATUS.success?
|
|
182
|
+
|
|
183
|
+
output.split("\n").map do |line|
|
|
184
|
+
{ name: line.strip, size: 0, compressed_size: 0,
|
|
185
|
+
is_dir: false, mtime: nil }
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Extract entry with gem
|
|
190
|
+
def extract_entry_with_gem(archive_path, entry_name,
|
|
191
|
+
output_path, password)
|
|
192
|
+
require "unrar"
|
|
193
|
+
archive = Unrar::Archive.new(archive_path)
|
|
194
|
+
archive.extract_to_file(entry_name, output_path,
|
|
195
|
+
password: password)
|
|
196
|
+
rescue StandardError => e
|
|
197
|
+
raise "Gem entry extraction failed: #{e.message}"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Extract entry with command
|
|
201
|
+
def extract_entry_with_command(archive_path, entry_name,
|
|
202
|
+
output_path, password)
|
|
203
|
+
temp_dir = Dir.mktmpdir
|
|
204
|
+
cmd = build_extract_command(archive_path, temp_dir, password)
|
|
205
|
+
unless system(cmd)
|
|
206
|
+
raise "Command entry extraction failed: #{entry_name}"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
source = File.join(temp_dir, entry_name)
|
|
210
|
+
FileUtils.mv(source, output_path) if File.exist?(source)
|
|
211
|
+
ensure
|
|
212
|
+
FileUtils.rm_rf(temp_dir) if temp_dir
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Build extract command
|
|
216
|
+
def build_extract_command(archive_path, output_dir, password)
|
|
217
|
+
cmd = "#{command_path} x -y"
|
|
218
|
+
cmd += " -p#{password}" if password
|
|
219
|
+
cmd += " \"#{archive_path}\" \"#{output_dir}/\""
|
|
220
|
+
cmd
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Unsupported error message
|
|
224
|
+
def unsupported_error
|
|
225
|
+
<<~ERROR
|
|
226
|
+
RAR extraction not available.
|
|
227
|
+
|
|
228
|
+
To enable RAR support, install one of:
|
|
229
|
+
1. unrar gem: gem install unrar
|
|
230
|
+
2. System unrar: brew install unrar (macOS) or
|
|
231
|
+
apt-get install unrar (Linux)
|
|
232
|
+
ERROR
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require_relative "license_validator"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
module Rar
|
|
10
|
+
# External RAR writer using licensed WinRAR installation
|
|
11
|
+
#
|
|
12
|
+
# This class provides RAR archive creation by wrapping the user's
|
|
13
|
+
# licensed WinRAR command-line tool. It does NOT implement RAR
|
|
14
|
+
# compression internally due to proprietary licensing restrictions.
|
|
15
|
+
#
|
|
16
|
+
# @example Create a RAR archive
|
|
17
|
+
# writer = ExternalWriter.new('archive.rar')
|
|
18
|
+
# writer.add_file('document.pdf')
|
|
19
|
+
# writer.add_directory('photos/')
|
|
20
|
+
# writer.write
|
|
21
|
+
#
|
|
22
|
+
# @example Create with options
|
|
23
|
+
# writer = ExternalWriter.new('archive.rar',
|
|
24
|
+
# compression: :best,
|
|
25
|
+
# solid: true,
|
|
26
|
+
# recovery: 5
|
|
27
|
+
# )
|
|
28
|
+
class ExternalWriter
|
|
29
|
+
# @return [String] Output archive path
|
|
30
|
+
attr_reader :output_path
|
|
31
|
+
|
|
32
|
+
# @return [Hash] Compression options
|
|
33
|
+
attr_reader :options
|
|
34
|
+
|
|
35
|
+
# @return [Array<String>] Files to add
|
|
36
|
+
attr_reader :files
|
|
37
|
+
|
|
38
|
+
# Check if RAR creation is available
|
|
39
|
+
#
|
|
40
|
+
# @return [Boolean] true if WinRAR executable found
|
|
41
|
+
def self.available?
|
|
42
|
+
!find_rar_executable.nil?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get RAR executable information
|
|
46
|
+
#
|
|
47
|
+
# @return [Hash] Executable path and version
|
|
48
|
+
def self.info
|
|
49
|
+
exe = find_rar_executable
|
|
50
|
+
return { available: false } unless exe
|
|
51
|
+
|
|
52
|
+
version_output, = Open3.capture2e(exe)
|
|
53
|
+
version = version_output.match(/RAR\s+([\d.]+)/i)&.[](1) || "unknown"
|
|
54
|
+
|
|
55
|
+
{
|
|
56
|
+
available: true,
|
|
57
|
+
executable: exe,
|
|
58
|
+
version: version,
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Find RAR executable on system
|
|
63
|
+
#
|
|
64
|
+
# @return [String, nil] Path to executable or nil
|
|
65
|
+
def self.find_rar_executable
|
|
66
|
+
if RUBY_PLATFORM.match?(/win32|mingw/)
|
|
67
|
+
find_windows_rar
|
|
68
|
+
else
|
|
69
|
+
find_unix_rar
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Initialize RAR writer
|
|
74
|
+
#
|
|
75
|
+
# @param output_path [String] Output RAR file path
|
|
76
|
+
# @param options [Hash] Compression options
|
|
77
|
+
# @option options [Symbol] :compression Compression level
|
|
78
|
+
# (:store, :fastest, :fast, :normal, :good, :best)
|
|
79
|
+
# @option options [Boolean] :solid Create solid archive
|
|
80
|
+
# @option options [Integer] :recovery Recovery record percentage (0-10)
|
|
81
|
+
# @option options [Boolean] :encrypt_headers Encrypt file names
|
|
82
|
+
# @option options [String] :password Archive password
|
|
83
|
+
# @option options [Integer] :volume_size Split into volumes (bytes)
|
|
84
|
+
# @option options [Boolean] :test_after_create Test archive after creation
|
|
85
|
+
# @option options [Boolean] :license_confirmed Skip license confirmation
|
|
86
|
+
def initialize(output_path, options = {})
|
|
87
|
+
@output_path = output_path
|
|
88
|
+
@options = default_options.merge(options)
|
|
89
|
+
@files = []
|
|
90
|
+
@directories = []
|
|
91
|
+
|
|
92
|
+
validate_availability!
|
|
93
|
+
validate_license! unless @options[:license_confirmed]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Add file to archive
|
|
97
|
+
#
|
|
98
|
+
# @param file_path [String] Path to file
|
|
99
|
+
# @param archive_path [String, nil] Path within archive
|
|
100
|
+
# @raise [ArgumentError] if file does not exist
|
|
101
|
+
def add_file(file_path, archive_path = nil)
|
|
102
|
+
raise ArgumentError, "File not found: #{file_path}" unless
|
|
103
|
+
File.exist?(file_path)
|
|
104
|
+
|
|
105
|
+
@files << {
|
|
106
|
+
source: File.expand_path(file_path),
|
|
107
|
+
archive_path: archive_path,
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Add directory to archive
|
|
112
|
+
#
|
|
113
|
+
# @param dir_path [String] Path to directory
|
|
114
|
+
# @param recursive [Boolean] Include subdirectories
|
|
115
|
+
# @param archive_path [String, nil] Path within archive
|
|
116
|
+
# @raise [ArgumentError] if directory does not exist
|
|
117
|
+
def add_directory(dir_path, recursive: true, archive_path: nil)
|
|
118
|
+
raise ArgumentError, "Directory not found: #{dir_path}" unless
|
|
119
|
+
Dir.exist?(dir_path)
|
|
120
|
+
|
|
121
|
+
@directories << {
|
|
122
|
+
source: File.expand_path(dir_path),
|
|
123
|
+
recursive: recursive,
|
|
124
|
+
archive_path: archive_path,
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Create RAR archive
|
|
129
|
+
#
|
|
130
|
+
# @raise [RarNotAvailableError] if WinRAR not available
|
|
131
|
+
# @raise [RuntimeError] if archive creation fails
|
|
132
|
+
def write
|
|
133
|
+
raise RarNotAvailableError unless self.class.available?
|
|
134
|
+
|
|
135
|
+
# Build command
|
|
136
|
+
cmd = build_command
|
|
137
|
+
|
|
138
|
+
# Execute RAR
|
|
139
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
|
140
|
+
|
|
141
|
+
unless status.success?
|
|
142
|
+
raise "RAR creation failed: #{stderr}\n#{stdout}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Test archive if requested
|
|
146
|
+
test_archive if @options[:test_after_create]
|
|
147
|
+
|
|
148
|
+
@output_path
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
# Default compression options
|
|
154
|
+
#
|
|
155
|
+
# @return [Hash] Default options
|
|
156
|
+
def default_options
|
|
157
|
+
{
|
|
158
|
+
compression: :normal,
|
|
159
|
+
solid: false,
|
|
160
|
+
recovery: 0,
|
|
161
|
+
encrypt_headers: false,
|
|
162
|
+
password: nil,
|
|
163
|
+
volume_size: nil,
|
|
164
|
+
test_after_create: false,
|
|
165
|
+
license_confirmed: false,
|
|
166
|
+
}
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Validate WinRAR availability
|
|
170
|
+
#
|
|
171
|
+
# @raise [RarNotAvailableError] if not available
|
|
172
|
+
def validate_availability!
|
|
173
|
+
raise RarNotAvailableError unless self.class.available?
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Validate license ownership
|
|
177
|
+
#
|
|
178
|
+
# @raise [NotLicensedError] if license not confirmed
|
|
179
|
+
def validate_license!
|
|
180
|
+
return if LicenseValidator.license_confirmed?
|
|
181
|
+
|
|
182
|
+
return if LicenseValidator.confirm_license!
|
|
183
|
+
|
|
184
|
+
raise NotLicensedError
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Build RAR command line
|
|
188
|
+
#
|
|
189
|
+
# @return [Array<String>] Command and arguments
|
|
190
|
+
def build_command
|
|
191
|
+
cmd = [rar_executable]
|
|
192
|
+
|
|
193
|
+
# Command: a = add
|
|
194
|
+
cmd << "a"
|
|
195
|
+
|
|
196
|
+
# Compression method
|
|
197
|
+
cmd << compression_switch
|
|
198
|
+
|
|
199
|
+
# Solid archive
|
|
200
|
+
cmd << "-s" if @options[:solid]
|
|
201
|
+
|
|
202
|
+
# Recovery record
|
|
203
|
+
cmd << "-rr#{@options[:recovery]}%" if @options[:recovery].positive?
|
|
204
|
+
|
|
205
|
+
# Password
|
|
206
|
+
if @options[:password]
|
|
207
|
+
cmd << "-p#{@options[:password]}"
|
|
208
|
+
cmd << "-hp" if @options[:encrypt_headers]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Volume size
|
|
212
|
+
cmd << "-v#{@options[:volume_size]}b" if @options[:volume_size]
|
|
213
|
+
|
|
214
|
+
# Overwrite existing
|
|
215
|
+
cmd << "-o+"
|
|
216
|
+
|
|
217
|
+
# Output archive path
|
|
218
|
+
cmd << @output_path
|
|
219
|
+
|
|
220
|
+
# Add files and directories
|
|
221
|
+
@files.each do |file_info|
|
|
222
|
+
cmd << if file_info[:archive_path]
|
|
223
|
+
# RAR doesn't support renaming in command line easily
|
|
224
|
+
# Would need to use temporary directory structure
|
|
225
|
+
end
|
|
226
|
+
file_info[:source]
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
@directories.each do |dir_info|
|
|
230
|
+
cmd << "-r" if dir_info[:recursive]
|
|
231
|
+
cmd << "#{dir_info[:source]}/*"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
cmd
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Get compression switch for level
|
|
238
|
+
#
|
|
239
|
+
# @return [String] Compression switch
|
|
240
|
+
def compression_switch
|
|
241
|
+
case @options[:compression]
|
|
242
|
+
when :store then "-m0"
|
|
243
|
+
when :fastest then "-m1"
|
|
244
|
+
when :fast then "-m2"
|
|
245
|
+
when :normal then "-m3"
|
|
246
|
+
when :good then "-m4"
|
|
247
|
+
when :best then "-m5"
|
|
248
|
+
else "-m3"
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Test archive integrity
|
|
253
|
+
#
|
|
254
|
+
# @raise [RuntimeError] if test fails
|
|
255
|
+
def test_archive
|
|
256
|
+
cmd = [rar_executable, "t", @output_path]
|
|
257
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
|
258
|
+
|
|
259
|
+
return if status.success?
|
|
260
|
+
|
|
261
|
+
raise "Archive test failed: #{stderr}\n#{stdout}"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Get RAR executable path
|
|
265
|
+
#
|
|
266
|
+
# @return [String] Path to executable
|
|
267
|
+
def rar_executable
|
|
268
|
+
self.class.find_rar_executable
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Find WinRAR on Windows
|
|
272
|
+
#
|
|
273
|
+
# @return [String, nil] Path or nil
|
|
274
|
+
def self.find_windows_rar
|
|
275
|
+
# Check common installation paths
|
|
276
|
+
paths = [
|
|
277
|
+
"C:\\Program Files\\WinRAR\\Rar.exe",
|
|
278
|
+
"C:\\Program Files (x86)\\WinRAR\\Rar.exe",
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
# Check PATH
|
|
282
|
+
path_rar = which("rar.exe") || which("Rar.exe")
|
|
283
|
+
paths.unshift(path_rar) if path_rar
|
|
284
|
+
|
|
285
|
+
paths.find { |path| File.exist?(path) }
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Find RAR on Unix systems
|
|
289
|
+
#
|
|
290
|
+
# @return [String, nil] Path or nil
|
|
291
|
+
def self.find_unix_rar
|
|
292
|
+
which("rar")
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Find executable in PATH
|
|
296
|
+
#
|
|
297
|
+
# @param cmd [String] Command name
|
|
298
|
+
# @return [String, nil] Path or nil
|
|
299
|
+
def self.which(cmd)
|
|
300
|
+
exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
|
301
|
+
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
|
|
302
|
+
exts.each do |ext|
|
|
303
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
|
304
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
nil
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|