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,485 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "entry"
|
|
5
|
+
require_relative "../formats/zip/reader"
|
|
6
|
+
require_relative "../formats/zip/writer"
|
|
7
|
+
require_relative "../extraction"
|
|
8
|
+
require_relative "../metadata/archive_metadata"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
module Zip
|
|
12
|
+
# Rubyzip-compatible File class
|
|
13
|
+
# Provides drop-in replacement for Zip::File from rubyzip
|
|
14
|
+
class File
|
|
15
|
+
include Enumerable
|
|
16
|
+
|
|
17
|
+
attr_reader :name, :entries, :comment
|
|
18
|
+
|
|
19
|
+
# Open a ZIP archive
|
|
20
|
+
# @param file_path [String] Path to ZIP file
|
|
21
|
+
# @param create [Boolean] Create file if it doesn't exist
|
|
22
|
+
# @param options [Hash] Additional options
|
|
23
|
+
# @yield [file] Block to execute with the opened archive
|
|
24
|
+
# @return [File] The opened archive (if no block given)
|
|
25
|
+
def self.open(file_path, create: false, **options)
|
|
26
|
+
file = new(file_path, create: create, **options)
|
|
27
|
+
|
|
28
|
+
if block_given?
|
|
29
|
+
begin
|
|
30
|
+
yield(file)
|
|
31
|
+
ensure
|
|
32
|
+
file.close
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
file
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Create a new archive from scratch
|
|
40
|
+
# @param file_path [String] Path to ZIP file
|
|
41
|
+
# @yield [file] Block to execute with the new archive
|
|
42
|
+
def self.create(file_path, &block)
|
|
43
|
+
open(file_path, create: true, &block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Initialize a ZIP file
|
|
47
|
+
# @param file_path [String] Path to ZIP file
|
|
48
|
+
# @param create [Boolean] Create file if it doesn't exist
|
|
49
|
+
def initialize(file_path, create: false, **options)
|
|
50
|
+
@name = file_path
|
|
51
|
+
@entries = []
|
|
52
|
+
@comment = ""
|
|
53
|
+
@create = create
|
|
54
|
+
@options = options
|
|
55
|
+
@modified = false
|
|
56
|
+
@reader = nil
|
|
57
|
+
@writer = nil
|
|
58
|
+
|
|
59
|
+
# Load existing archive if it exists
|
|
60
|
+
if ::File.exist?(file_path) && !create
|
|
61
|
+
load_archive
|
|
62
|
+
elsif create
|
|
63
|
+
# Will create on write/close
|
|
64
|
+
@modified = true
|
|
65
|
+
elsif !::File.exist?(file_path)
|
|
66
|
+
raise Errno::ENOENT, "No such file or directory - #{file_path}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Add a file to the archive
|
|
71
|
+
# @param entry_name [String] Name in the archive
|
|
72
|
+
# @param src_path [String, nil] Source file path (optional if block given)
|
|
73
|
+
# @yield Block that returns content to add
|
|
74
|
+
def add(entry_name, src_path = nil)
|
|
75
|
+
# Handle directory entries (ending with /)
|
|
76
|
+
if entry_name.end_with?("/") && !src_path && !block_given?
|
|
77
|
+
add_directory(entry_name)
|
|
78
|
+
elsif block_given?
|
|
79
|
+
data = yield
|
|
80
|
+
add_data(entry_name, data)
|
|
81
|
+
elsif src_path
|
|
82
|
+
add_file_from_path(entry_name, src_path)
|
|
83
|
+
else
|
|
84
|
+
raise ArgumentError, "Either src_path or block must be provided"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
@modified = true
|
|
88
|
+
self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get entry by name
|
|
92
|
+
# @param entry_name [String] Name of the entry
|
|
93
|
+
# @return [Entry, nil] The entry or nil if not found
|
|
94
|
+
def get_entry(entry_name)
|
|
95
|
+
entries.find { |e| e.name == entry_name }
|
|
96
|
+
end
|
|
97
|
+
alias_method :find_entry, :get_entry
|
|
98
|
+
|
|
99
|
+
# Get input stream for an entry
|
|
100
|
+
# @param entry [Entry, String] Entry object or name
|
|
101
|
+
# @yield [stream] Block to read from the stream
|
|
102
|
+
# @return [String] Entry content (if no block given)
|
|
103
|
+
# rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding, Style/BlockDelimiters -- Ruby 3.0 compatibility
|
|
104
|
+
def get_input_stream(entry, &block)
|
|
105
|
+
entry = get_entry(entry) if entry.is_a?(String)
|
|
106
|
+
raise Errno::ENOENT, "Entry not found: #{entry}" unless entry
|
|
107
|
+
|
|
108
|
+
content = read_entry_data(entry)
|
|
109
|
+
|
|
110
|
+
if block
|
|
111
|
+
require "stringio"
|
|
112
|
+
StringIO.open(content, "rb", &block)
|
|
113
|
+
else
|
|
114
|
+
content
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
# rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding, Style/BlockDelimiters
|
|
118
|
+
alias_method :read, :get_input_stream
|
|
119
|
+
|
|
120
|
+
# Iterate over all entries
|
|
121
|
+
# @yield [entry] Block to execute for each entry
|
|
122
|
+
# rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
|
|
123
|
+
def each(&block)
|
|
124
|
+
entries.each(&block)
|
|
125
|
+
end
|
|
126
|
+
# rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
|
|
127
|
+
|
|
128
|
+
# Extract an entry to a destination path
|
|
129
|
+
# @param entry [Entry, String] Entry object or name
|
|
130
|
+
# @param dest_path [String] Destination file path
|
|
131
|
+
def extract(entry, dest_path, &on_exists_proc)
|
|
132
|
+
entry = get_entry(entry) if entry.is_a?(String)
|
|
133
|
+
raise Errno::ENOENT, "Entry not found: #{entry}" unless entry
|
|
134
|
+
|
|
135
|
+
# Handle existing file
|
|
136
|
+
if ::File.exist?(dest_path)
|
|
137
|
+
if on_exists_proc
|
|
138
|
+
action = yield(entry, dest_path)
|
|
139
|
+
return if action == false
|
|
140
|
+
else
|
|
141
|
+
raise "Destination file already exists: #{dest_path}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
extract_entry_to_path(entry, dest_path)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Remove an entry from the archive
|
|
149
|
+
# @param entry_name [String] Name of the entry to remove
|
|
150
|
+
def remove(entry_name)
|
|
151
|
+
entry = get_entry(entry_name)
|
|
152
|
+
@entries.delete(entry) if entry
|
|
153
|
+
@modified = true
|
|
154
|
+
@reader = nil
|
|
155
|
+
self
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Rename an entry
|
|
159
|
+
# @param entry_name [String] Current entry name
|
|
160
|
+
# @param new_name [String] New entry name
|
|
161
|
+
def rename(entry_name, new_name)
|
|
162
|
+
entry = get_entry(entry_name)
|
|
163
|
+
return unless entry
|
|
164
|
+
|
|
165
|
+
entry.header.filename = new_name
|
|
166
|
+
@modified = true
|
|
167
|
+
self
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Replace entry content
|
|
171
|
+
# @param entry_name [String] Entry name
|
|
172
|
+
# @param src_path [String, nil] Source file path
|
|
173
|
+
# rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
|
|
174
|
+
def replace(entry_name, src_path = nil, &block)
|
|
175
|
+
remove(entry_name)
|
|
176
|
+
add(entry_name, src_path, &block)
|
|
177
|
+
end
|
|
178
|
+
# rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
|
|
179
|
+
|
|
180
|
+
# Get archive comment
|
|
181
|
+
def comment
|
|
182
|
+
@comment
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Set archive comment
|
|
186
|
+
def comment=(value)
|
|
187
|
+
@comment = value.to_s
|
|
188
|
+
@modified = true
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Get number of entries
|
|
192
|
+
def size
|
|
193
|
+
entries.size
|
|
194
|
+
end
|
|
195
|
+
alias_method :length, :size
|
|
196
|
+
|
|
197
|
+
# Check if archive contains an entry
|
|
198
|
+
# @param entry_name [String] Entry name to check
|
|
199
|
+
# @return [Boolean] True if entry exists
|
|
200
|
+
def include?(entry_name)
|
|
201
|
+
!get_entry(entry_name).nil?
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Get names of all entries
|
|
205
|
+
# @return [Array<String>] Array of entry names
|
|
206
|
+
def names
|
|
207
|
+
entries.map(&:name)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Glob entries by pattern
|
|
211
|
+
# @param pattern [String] Glob pattern
|
|
212
|
+
# @return [Array<Entry>] Matching entries
|
|
213
|
+
# rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding, Style/BlockDelimiters -- Ruby 3.0 compatibility
|
|
214
|
+
def glob(pattern, &block)
|
|
215
|
+
matching = entries.select { |e| ::File.fnmatch(pattern, e.name) }
|
|
216
|
+
|
|
217
|
+
if block
|
|
218
|
+
matching.each(&block)
|
|
219
|
+
else
|
|
220
|
+
matching
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
# rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding, Style/BlockDelimiters
|
|
224
|
+
|
|
225
|
+
# Extract files matching a pattern
|
|
226
|
+
# @param pattern [String, Regexp, Array] Pattern(s) to match
|
|
227
|
+
# @param dest [String] Destination directory
|
|
228
|
+
# @param options [Hash] Extraction options
|
|
229
|
+
# @option options [Boolean] :preserve_paths Keep directory structure
|
|
230
|
+
# @option options [Boolean] :flatten Extract all to destination root
|
|
231
|
+
# @option options [Boolean] :overwrite Overwrite existing files
|
|
232
|
+
# @return [Array<String>] Paths of extracted files
|
|
233
|
+
def extract_matching(pattern, dest, options = {})
|
|
234
|
+
Omnizip::Extraction.extract_matching(self, pattern, dest, options)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Extract files matching a pattern to memory
|
|
238
|
+
# @param pattern [String, Regexp, Array] Pattern(s) to match
|
|
239
|
+
# @return [Hash<String, String>] Hash of filename => content
|
|
240
|
+
def extract_matching_to_memory(pattern)
|
|
241
|
+
Omnizip::Extraction.extract_to_memory_matching(self, pattern)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# List files matching a pattern
|
|
245
|
+
# @param pattern [String, Regexp, Array] Pattern(s) to match
|
|
246
|
+
# @return [Array<Entry>] Matching entries
|
|
247
|
+
def list_matching(pattern)
|
|
248
|
+
Omnizip::Extraction.list_matching(self, pattern)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Count files matching a pattern
|
|
252
|
+
# @param pattern [String, Regexp, Array] Pattern(s) to match
|
|
253
|
+
# @return [Integer] Number of matches
|
|
254
|
+
def count_matching(pattern)
|
|
255
|
+
Omnizip::Extraction.count_matching(self, pattern)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Extract with a filter chain
|
|
259
|
+
# @param filter [Omnizip::Extraction::FilterChain] Filter chain
|
|
260
|
+
# @param dest [String] Destination directory
|
|
261
|
+
# @param options [Hash] Extraction options
|
|
262
|
+
# @return [Array<String>] Paths of extracted files
|
|
263
|
+
def extract_with_filter(filter, dest, options = {})
|
|
264
|
+
Omnizip::Extraction.extract_with_filter(self, filter, dest, options)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Get archive metadata
|
|
268
|
+
# @return [Omnizip::Metadata::ArchiveMetadata] Archive metadata
|
|
269
|
+
def metadata
|
|
270
|
+
@metadata ||= Omnizip::Metadata::ArchiveMetadata.new(self)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Save metadata changes
|
|
274
|
+
# Marks the archive as modified so changes are written on close
|
|
275
|
+
def save_metadata
|
|
276
|
+
@modified = true
|
|
277
|
+
metadata.reset_modified
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Commit changes to disk
|
|
281
|
+
def commit
|
|
282
|
+
write_archive if @modified
|
|
283
|
+
@modified = false
|
|
284
|
+
@reader = nil # Invalidate reader after writing
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Close the archive
|
|
288
|
+
def close
|
|
289
|
+
commit if @modified
|
|
290
|
+
@reader = nil
|
|
291
|
+
@writer = nil
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
private
|
|
295
|
+
|
|
296
|
+
# Load existing archive
|
|
297
|
+
def load_archive
|
|
298
|
+
@reader = Omnizip::Formats::Zip::Reader.new(@name)
|
|
299
|
+
@reader.read
|
|
300
|
+
|
|
301
|
+
@entries = @reader.entries.map do |header|
|
|
302
|
+
Entry.new(header, filepath: @name)
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Add file from filesystem path
|
|
307
|
+
def add_file_from_path(entry_name, src_path)
|
|
308
|
+
unless ::File.exist?(src_path)
|
|
309
|
+
raise Errno::ENOENT, "Source file not found: #{src_path}"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
if ::File.directory?(src_path)
|
|
313
|
+
add_directory(entry_name)
|
|
314
|
+
else
|
|
315
|
+
data = ::File.binread(src_path)
|
|
316
|
+
stat = ::File.stat(src_path)
|
|
317
|
+
add_data(entry_name, data, stat: stat)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Add data directly
|
|
322
|
+
def add_data(entry_name, data, stat: nil)
|
|
323
|
+
# Remove existing entry with same name
|
|
324
|
+
remove(entry_name)
|
|
325
|
+
|
|
326
|
+
# Create new header
|
|
327
|
+
header = create_header(entry_name, data, stat: stat)
|
|
328
|
+
entry = Entry.new(header, filepath: @name)
|
|
329
|
+
@entries << entry
|
|
330
|
+
|
|
331
|
+
# Invalidate reader cache since we modified entries
|
|
332
|
+
@reader = nil
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Add directory entry
|
|
336
|
+
def add_directory(entry_name)
|
|
337
|
+
entry_name = "#{entry_name}/" unless entry_name.end_with?("/")
|
|
338
|
+
header = create_header(entry_name, "", directory: true)
|
|
339
|
+
entry = Entry.new(header, filepath: @name)
|
|
340
|
+
@entries << entry
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Create a central directory header
|
|
344
|
+
def create_header(filename, data, stat: nil, directory: false)
|
|
345
|
+
require_relative "../formats/zip/central_directory_header"
|
|
346
|
+
|
|
347
|
+
now = Time.now
|
|
348
|
+
crc32 = if directory
|
|
349
|
+
0
|
|
350
|
+
else
|
|
351
|
+
Omnizip::Checksums::Crc32.new.tap do |c|
|
|
352
|
+
c.update(data)
|
|
353
|
+
end.value
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
external_attrs = if directory
|
|
357
|
+
Omnizip::Formats::Zip::Constants::UNIX_DIR_PERMISSIONS |
|
|
358
|
+
Omnizip::Formats::Zip::Constants::ATTR_DIRECTORY
|
|
359
|
+
elsif stat
|
|
360
|
+
(stat.mode & 0o777) << 16
|
|
361
|
+
else
|
|
362
|
+
Omnizip::Formats::Zip::Constants::UNIX_FILE_PERMISSIONS
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
Omnizip::Formats::Zip::CentralDirectoryHeader.new(
|
|
366
|
+
version_made_by: Omnizip::Formats::Zip::Constants::VERSION_MADE_BY_UNIX |
|
|
367
|
+
Omnizip::Formats::Zip::Constants::VERSION_DEFAULT,
|
|
368
|
+
version_needed: Omnizip::Formats::Zip::Constants::VERSION_DEFAULT,
|
|
369
|
+
flags: Omnizip::Formats::Zip::Constants::FLAG_UTF8,
|
|
370
|
+
compression_method: directory ? 0 : 8, # Store or Deflate
|
|
371
|
+
last_mod_time: dos_time(now),
|
|
372
|
+
last_mod_date: dos_date(now),
|
|
373
|
+
crc32: crc32,
|
|
374
|
+
compressed_size: 0, # Will be set during write
|
|
375
|
+
uncompressed_size: data.bytesize,
|
|
376
|
+
disk_number_start: 0,
|
|
377
|
+
internal_attributes: 0,
|
|
378
|
+
external_attributes: external_attrs,
|
|
379
|
+
local_header_offset: 0, # Will be set during write
|
|
380
|
+
filename: filename,
|
|
381
|
+
extra_field: "",
|
|
382
|
+
comment: "",
|
|
383
|
+
).tap do |header|
|
|
384
|
+
# Store original data in header for writing
|
|
385
|
+
header.instance_variable_set(:@_original_data, data)
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Read entry data from archive
|
|
390
|
+
def read_entry_data(entry)
|
|
391
|
+
return "" if entry.directory?
|
|
392
|
+
|
|
393
|
+
# Check if we have cached data (for new entries not yet written)
|
|
394
|
+
cached_data = entry.header.instance_variable_get(:@_original_data)
|
|
395
|
+
return cached_data if cached_data
|
|
396
|
+
|
|
397
|
+
# Otherwise read from file
|
|
398
|
+
unless @reader
|
|
399
|
+
@reader = Omnizip::Formats::Zip::Reader.new(@name)
|
|
400
|
+
@reader.read
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
::File.open(@name, "rb") do |io|
|
|
404
|
+
# Find the entry in reader
|
|
405
|
+
reader_entry = @reader.entries.find { |e| e.filename == entry.name }
|
|
406
|
+
raise "Entry not found in archive: #{entry.name}" unless reader_entry
|
|
407
|
+
|
|
408
|
+
# Extract just the data
|
|
409
|
+
io.seek(reader_entry.local_header_offset, ::IO::SEEK_SET)
|
|
410
|
+
|
|
411
|
+
# Read and parse local file header
|
|
412
|
+
fixed_header = io.read(30)
|
|
413
|
+
return "" unless fixed_header && fixed_header.size == 30
|
|
414
|
+
|
|
415
|
+
_signature, _version, _flags, _method, _time, _date, _crc32,
|
|
416
|
+
_comp_size, _uncomp_size, filename_length, extra_length = fixed_header.unpack("VvvvvvVVVvv")
|
|
417
|
+
|
|
418
|
+
# Skip filename and extra field
|
|
419
|
+
io.read(filename_length + extra_length)
|
|
420
|
+
|
|
421
|
+
# Read compressed data
|
|
422
|
+
compressed_data = io.read(reader_entry.compressed_size)
|
|
423
|
+
return "" unless compressed_data
|
|
424
|
+
|
|
425
|
+
# Decompress
|
|
426
|
+
@reader.send(:decompress_data,
|
|
427
|
+
compressed_data,
|
|
428
|
+
reader_entry.compression_method,
|
|
429
|
+
reader_entry.uncompressed_size)
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Extract entry to filesystem
|
|
434
|
+
def extract_entry_to_path(entry, dest_path)
|
|
435
|
+
if entry.directory?
|
|
436
|
+
FileUtils.mkdir_p(dest_path)
|
|
437
|
+
else
|
|
438
|
+
FileUtils.mkdir_p(::File.dirname(dest_path))
|
|
439
|
+
content = read_entry_data(entry)
|
|
440
|
+
::File.binwrite(dest_path, content)
|
|
441
|
+
|
|
442
|
+
# Set permissions if Unix
|
|
443
|
+
if entry.unix_perms.positive?
|
|
444
|
+
::File.chmod(entry.unix_perms & 0o777, dest_path)
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Write archive to disk
|
|
450
|
+
def write_archive
|
|
451
|
+
# Cache all entry data before writing (to avoid reading while writing)
|
|
452
|
+
entry_data = {}
|
|
453
|
+
entries.each do |entry|
|
|
454
|
+
next if entry.directory?
|
|
455
|
+
|
|
456
|
+
header = entry.header
|
|
457
|
+
cached = header.instance_variable_get(:@_original_data)
|
|
458
|
+
entry_data[entry.name] = cached || read_entry_data(entry)
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
writer = Omnizip::Formats::Zip::Writer.new(@name)
|
|
462
|
+
|
|
463
|
+
entries.each do |entry|
|
|
464
|
+
if entry.directory?
|
|
465
|
+
writer.add_directory(entry.name)
|
|
466
|
+
else
|
|
467
|
+
writer.add_data(entry.name, entry_data[entry.name])
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
writer.write
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# Convert Time to DOS time format
|
|
475
|
+
def dos_time(time)
|
|
476
|
+
((time.hour << 11) | (time.min << 5) | (time.sec / 2)) & 0xFFFF
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Convert Time to DOS date format
|
|
480
|
+
def dos_date(time)
|
|
481
|
+
(((time.year - 1980) << 9) | (time.month << 5) | time.day) & 0xFFFF
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|