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,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Pipe
|
|
7
|
+
# Stream-based decompression for pipe operations
|
|
8
|
+
#
|
|
9
|
+
# Accepts compressed archive from any IO-like source and extracts
|
|
10
|
+
# to directory or streams to output. Handles multi-file archives
|
|
11
|
+
# and provides error recovery for corrupted streams.
|
|
12
|
+
#
|
|
13
|
+
# @example Decompress to directory
|
|
14
|
+
# decompressor = StreamDecompressor.new($stdin, output_dir: 'extracted/')
|
|
15
|
+
# decompressor.decompress
|
|
16
|
+
#
|
|
17
|
+
# @example Decompress to stdout
|
|
18
|
+
# decompressor = StreamDecompressor.new($stdin, output: $stdout)
|
|
19
|
+
# decompressor.decompress
|
|
20
|
+
class StreamDecompressor
|
|
21
|
+
# Default chunk size for streaming extraction (64KB)
|
|
22
|
+
DEFAULT_CHUNK_SIZE = 64 * 1024
|
|
23
|
+
|
|
24
|
+
attr_reader :input, :output_dir, :output, :options, :bytes_written
|
|
25
|
+
|
|
26
|
+
# Initialize stream decompressor
|
|
27
|
+
#
|
|
28
|
+
# @param input [IO] Input stream containing archive
|
|
29
|
+
# @param output_dir [String, nil] Directory to extract to
|
|
30
|
+
# @param output [IO, nil] Output stream (for single-file extraction)
|
|
31
|
+
# @param options [Hash] Decompression options
|
|
32
|
+
# @option options [Symbol] :format Archive format (auto-detected if nil)
|
|
33
|
+
# @option options [Integer] :chunk_size Write buffer size
|
|
34
|
+
# @option options [Proc] :progress Progress callback
|
|
35
|
+
# @option options [Boolean] :preserve_paths Preserve directory structure
|
|
36
|
+
def initialize(input, output_dir: nil, output: nil, **options)
|
|
37
|
+
@input = input
|
|
38
|
+
@output_dir = output_dir
|
|
39
|
+
@output = output
|
|
40
|
+
@options = options
|
|
41
|
+
@chunk_size = options[:chunk_size] || DEFAULT_CHUNK_SIZE
|
|
42
|
+
@bytes_written = 0
|
|
43
|
+
@progress_callback = options[:progress]
|
|
44
|
+
@format = options[:format]
|
|
45
|
+
@preserve_paths = options.fetch(:preserve_paths, true)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Decompress input stream
|
|
49
|
+
#
|
|
50
|
+
# Extracts archive to output directory or streams to output.
|
|
51
|
+
# Returns hash of extracted files or bytes written.
|
|
52
|
+
#
|
|
53
|
+
# @return [Hash<String, Integer>, Integer] Files => bytes or total bytes
|
|
54
|
+
# @raise [Omnizip::FormatError] If archive format is invalid
|
|
55
|
+
def decompress
|
|
56
|
+
if @output
|
|
57
|
+
decompress_to_stream
|
|
58
|
+
elsif @output_dir
|
|
59
|
+
decompress_to_directory
|
|
60
|
+
else
|
|
61
|
+
raise ArgumentError,
|
|
62
|
+
"Either output_dir or output must be specified"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# Decompress to output stream
|
|
69
|
+
#
|
|
70
|
+
# Extracts first non-directory entry to output stream.
|
|
71
|
+
# Useful for single-file archives or piping to stdout.
|
|
72
|
+
#
|
|
73
|
+
# @return [Integer] Bytes written to output
|
|
74
|
+
def decompress_to_stream
|
|
75
|
+
# Read archive into memory (necessary for format detection)
|
|
76
|
+
archive_data = @input.read
|
|
77
|
+
|
|
78
|
+
Omnizip::Buffer.open(archive_data, format: @format) do |archive|
|
|
79
|
+
archive.each_entry do |entry|
|
|
80
|
+
next if entry.directory?
|
|
81
|
+
|
|
82
|
+
# Stream first file to output
|
|
83
|
+
while chunk = entry.read(@chunk_size)
|
|
84
|
+
@output.write(chunk)
|
|
85
|
+
@bytes_written += chunk.bytesize
|
|
86
|
+
|
|
87
|
+
@progress_callback&.call(entry.name, @bytes_written)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
break # Only extract first file for streaming
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
@bytes_written
|
|
95
|
+
rescue StandardError => e
|
|
96
|
+
raise Omnizip::Error,
|
|
97
|
+
"Stream decompression failed: #{e.message}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Decompress to directory
|
|
101
|
+
#
|
|
102
|
+
# Extracts all entries to specified directory with
|
|
103
|
+
# directory structure preservation.
|
|
104
|
+
#
|
|
105
|
+
# @return [Hash<String, Integer>] Filename => bytes mapping
|
|
106
|
+
def decompress_to_directory
|
|
107
|
+
FileUtils.mkdir_p(@output_dir)
|
|
108
|
+
|
|
109
|
+
# Read archive into memory
|
|
110
|
+
archive_data = @input.read
|
|
111
|
+
extracted_files = {}
|
|
112
|
+
|
|
113
|
+
Omnizip::Buffer.open(archive_data, format: @format) do |archive|
|
|
114
|
+
archive.each_entry do |entry|
|
|
115
|
+
if entry.directory?
|
|
116
|
+
create_directory(entry)
|
|
117
|
+
else
|
|
118
|
+
bytes = extract_file(entry)
|
|
119
|
+
extracted_files[entry.name] = bytes
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
extracted_files
|
|
125
|
+
rescue StandardError => e
|
|
126
|
+
raise Omnizip::Error,
|
|
127
|
+
"Directory extraction failed: #{e.message}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Create directory from entry
|
|
131
|
+
#
|
|
132
|
+
# @param entry [Object] Directory entry
|
|
133
|
+
def create_directory(entry)
|
|
134
|
+
return unless @preserve_paths
|
|
135
|
+
|
|
136
|
+
dir_path = File.join(@output_dir, entry.name)
|
|
137
|
+
FileUtils.mkdir_p(dir_path)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Extract file from entry
|
|
141
|
+
#
|
|
142
|
+
# @param entry [Object] File entry
|
|
143
|
+
# @return [Integer] Bytes written
|
|
144
|
+
def extract_file(entry)
|
|
145
|
+
dest_path = if @preserve_paths
|
|
146
|
+
File.join(@output_dir, entry.name)
|
|
147
|
+
else
|
|
148
|
+
File.join(@output_dir, File.basename(entry.name))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Create parent directory if needed
|
|
152
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
|
153
|
+
|
|
154
|
+
bytes = 0
|
|
155
|
+
File.open(dest_path, "wb") do |file|
|
|
156
|
+
while chunk = entry.read(@chunk_size)
|
|
157
|
+
file.write(chunk)
|
|
158
|
+
bytes += chunk.bytesize
|
|
159
|
+
@bytes_written += chunk.bytesize
|
|
160
|
+
|
|
161
|
+
@progress_callback&.call(entry.name, @bytes_written)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Preserve timestamp if available
|
|
166
|
+
if entry.respond_to?(:mtime) && entry.mtime
|
|
167
|
+
File.utime(entry.mtime, entry.mtime, dest_path)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
bytes
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
data/lib/omnizip/pipe.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "pipe/stream_compressor"
|
|
4
|
+
require_relative "pipe/stream_decompressor"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
# Unix pipe operations for stdin/stdout integration
|
|
8
|
+
#
|
|
9
|
+
# Enables compression and decompression through Unix pipes without
|
|
10
|
+
# temporary files, supporting container workflows, CI/CD pipelines,
|
|
11
|
+
# and stream processing scenarios.
|
|
12
|
+
#
|
|
13
|
+
# @example Compress stdin to stdout
|
|
14
|
+
# Omnizip::Pipe.compress($stdin, $stdout, format: :zip)
|
|
15
|
+
#
|
|
16
|
+
# @example Decompress stdin to directory
|
|
17
|
+
# Omnizip::Pipe.decompress($stdin, output_dir: 'extracted/')
|
|
18
|
+
#
|
|
19
|
+
# @example File-to-file via pipe
|
|
20
|
+
# File.open('input.txt', 'rb') do |input|
|
|
21
|
+
# File.open('output.zip', 'wb') do |output|
|
|
22
|
+
# Omnizip::Pipe.compress(input, output, format: :zip)
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
module Pipe
|
|
26
|
+
class << self
|
|
27
|
+
# Compress from input stream to output stream
|
|
28
|
+
#
|
|
29
|
+
# @param input [IO] Input stream (stdin, File, Socket, etc.)
|
|
30
|
+
# @param output [IO] Output stream (stdout, File, network, etc.)
|
|
31
|
+
# @param format [Symbol] Archive format (:zip, :seven_zip)
|
|
32
|
+
# @param compression [Symbol] Compression algorithm
|
|
33
|
+
# @param options [Hash] Additional compression options
|
|
34
|
+
# @option options [String] :entry_name Name for entry in archive
|
|
35
|
+
# @option options [Integer] :chunk_size Read buffer size (default 64KB)
|
|
36
|
+
# @option options [Integer] :level Compression level (1-9)
|
|
37
|
+
# @return [Integer] Number of bytes written
|
|
38
|
+
#
|
|
39
|
+
# @example Compress stdin to stdout
|
|
40
|
+
# Omnizip::Pipe.compress($stdin, $stdout, format: :zip)
|
|
41
|
+
#
|
|
42
|
+
# @example Compress file with custom options
|
|
43
|
+
# File.open('data.txt', 'rb') do |input|
|
|
44
|
+
# File.open('data.zip', 'wb') do |output|
|
|
45
|
+
# Omnizip::Pipe.compress(
|
|
46
|
+
# input, output,
|
|
47
|
+
# format: :zip,
|
|
48
|
+
# compression: :deflate,
|
|
49
|
+
# level: 9,
|
|
50
|
+
# entry_name: 'data.txt'
|
|
51
|
+
# )
|
|
52
|
+
# end
|
|
53
|
+
# end
|
|
54
|
+
def compress(input, output, format: :zip, compression: nil, **options)
|
|
55
|
+
compressor = Pipe::StreamCompressor.new(
|
|
56
|
+
input,
|
|
57
|
+
output,
|
|
58
|
+
format,
|
|
59
|
+
compression: compression,
|
|
60
|
+
**options,
|
|
61
|
+
)
|
|
62
|
+
compressor.compress
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Decompress from input stream
|
|
66
|
+
#
|
|
67
|
+
# @param input [IO] Input stream containing archive
|
|
68
|
+
# @param output_dir [String, nil] Directory to extract to (nil = stdout)
|
|
69
|
+
# @param options [Hash] Extraction options
|
|
70
|
+
# @return [Integer, Hash] Bytes written or extracted files hash
|
|
71
|
+
#
|
|
72
|
+
# @example Extract to directory
|
|
73
|
+
# Omnizip::Pipe.decompress($stdin, output_dir: 'extracted/')
|
|
74
|
+
#
|
|
75
|
+
# @example Extract to stdout (single file)
|
|
76
|
+
# Omnizip::Pipe.decompress($stdin, output: $stdout)
|
|
77
|
+
def decompress(input, output_dir: nil, output: nil, **options)
|
|
78
|
+
decompressor = Pipe::StreamDecompressor.new(
|
|
79
|
+
input,
|
|
80
|
+
output_dir: output_dir,
|
|
81
|
+
output: output,
|
|
82
|
+
**options,
|
|
83
|
+
)
|
|
84
|
+
decompressor.decompress
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Check if running in pipe mode
|
|
88
|
+
#
|
|
89
|
+
# Detects if stdin/stdout are pipes (not TTY), indicating
|
|
90
|
+
# the program is being used in a Unix pipeline.
|
|
91
|
+
#
|
|
92
|
+
# @return [Boolean] True if stdin and stdout are pipes
|
|
93
|
+
#
|
|
94
|
+
# @example
|
|
95
|
+
# if Omnizip::Pipe.pipe_mode?
|
|
96
|
+
# # Process stdin to stdout
|
|
97
|
+
# else
|
|
98
|
+
# # Show interactive help
|
|
99
|
+
# end
|
|
100
|
+
def pipe_mode?
|
|
101
|
+
!$stdin.tty? && !$stdout.tty?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Detect if input is stdin
|
|
105
|
+
#
|
|
106
|
+
# @param input [String, IO] Input path or IO object
|
|
107
|
+
# @return [Boolean] True if input is stdin
|
|
108
|
+
def stdin?(input)
|
|
109
|
+
["-", $stdin].include?(input)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Detect if output is stdout
|
|
113
|
+
#
|
|
114
|
+
# @param output [String, IO] Output path or IO object
|
|
115
|
+
# @return [Boolean] True if output is stdout
|
|
116
|
+
def stdout?(output)
|
|
117
|
+
["-", $stdout].include?(output)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
require_relative "../platform"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Platform
|
|
8
|
+
# NTFS Alternate Data Streams handler
|
|
9
|
+
# Windows-only feature for managing file alternate streams
|
|
10
|
+
module NtfsStreams
|
|
11
|
+
# Check if NTFS streams are available
|
|
12
|
+
#
|
|
13
|
+
# @return [Boolean] true if available
|
|
14
|
+
def self.available?
|
|
15
|
+
Platform.supports_ntfs_streams?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# List alternate data streams for a file
|
|
19
|
+
#
|
|
20
|
+
# @param file_path [String] Path to file
|
|
21
|
+
# @return [Array<String>] List of stream names
|
|
22
|
+
def self.list_streams(file_path)
|
|
23
|
+
return [] unless available?
|
|
24
|
+
return [] unless File.exist?(file_path)
|
|
25
|
+
|
|
26
|
+
streams = []
|
|
27
|
+
|
|
28
|
+
begin
|
|
29
|
+
# Use PowerShell to list streams
|
|
30
|
+
cmd = "powershell -Command \"Get-Item '#{file_path}' -Stream * | " \
|
|
31
|
+
"Select-Object -ExpandProperty Stream\""
|
|
32
|
+
output = `#{cmd} 2>&1`
|
|
33
|
+
|
|
34
|
+
if $CHILD_STATUS.success?
|
|
35
|
+
streams = output.lines.map(&:strip).reject do |s|
|
|
36
|
+
s.empty? || s == ":$DATA"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
warn "Failed to list NTFS streams: #{e.message}" if ENV["DEBUG"]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
streams
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Read alternate data stream
|
|
47
|
+
#
|
|
48
|
+
# @param file_path [String] Path to file
|
|
49
|
+
# @param stream_name [String] Stream name
|
|
50
|
+
# @return [String, nil] Stream content or nil
|
|
51
|
+
def self.read_stream(file_path, stream_name)
|
|
52
|
+
return nil unless available?
|
|
53
|
+
return nil unless File.exist?(file_path)
|
|
54
|
+
|
|
55
|
+
begin
|
|
56
|
+
# Read using alternate stream syntax: file.txt:StreamName
|
|
57
|
+
stream_path = "#{file_path}:#{stream_name}"
|
|
58
|
+
File.binread(stream_path)
|
|
59
|
+
rescue Errno::ENOENT, Errno::EINVAL
|
|
60
|
+
nil
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
warn "Failed to read NTFS stream: #{e.message}" if ENV["DEBUG"]
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Write alternate data stream
|
|
68
|
+
#
|
|
69
|
+
# @param file_path [String] Path to file
|
|
70
|
+
# @param stream_name [String] Stream name
|
|
71
|
+
# @param data [String] Stream data
|
|
72
|
+
# @return [Boolean] true if successful
|
|
73
|
+
def self.write_stream(file_path, stream_name, data)
|
|
74
|
+
return false unless available?
|
|
75
|
+
return false unless File.exist?(file_path)
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
# Write using alternate stream syntax: file.txt:StreamName
|
|
79
|
+
stream_path = "#{file_path}:#{stream_name}"
|
|
80
|
+
File.binwrite(stream_path, data)
|
|
81
|
+
true
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
warn "Failed to write NTFS stream: #{e.message}" if ENV["DEBUG"]
|
|
84
|
+
false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Delete alternate data stream
|
|
89
|
+
#
|
|
90
|
+
# @param file_path [String] Path to file
|
|
91
|
+
# @param stream_name [String] Stream name
|
|
92
|
+
# @return [Boolean] true if successful
|
|
93
|
+
def self.delete_stream(file_path, stream_name)
|
|
94
|
+
return false unless available?
|
|
95
|
+
return false unless File.exist?(file_path)
|
|
96
|
+
|
|
97
|
+
begin
|
|
98
|
+
# Use PowerShell to remove stream
|
|
99
|
+
cmd = "powershell -Command \"Remove-Item -Path '#{file_path}' " \
|
|
100
|
+
"-Stream '#{stream_name}'\""
|
|
101
|
+
system(cmd)
|
|
102
|
+
$CHILD_STATUS.success?
|
|
103
|
+
rescue StandardError => e
|
|
104
|
+
warn "Failed to delete NTFS stream: #{e.message}" if ENV["DEBUG"]
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Copy all alternate streams from source to destination
|
|
110
|
+
#
|
|
111
|
+
# @param source_path [String] Source file
|
|
112
|
+
# @param dest_path [String] Destination file
|
|
113
|
+
# @return [Integer] Number of streams copied
|
|
114
|
+
def self.copy_streams(source_path, dest_path)
|
|
115
|
+
return 0 unless available?
|
|
116
|
+
return 0 unless File.exist?(source_path)
|
|
117
|
+
return 0 unless File.exist?(dest_path)
|
|
118
|
+
|
|
119
|
+
copied = 0
|
|
120
|
+
streams = list_streams(source_path)
|
|
121
|
+
|
|
122
|
+
streams.each do |stream|
|
|
123
|
+
next if stream == "$DATA" # Skip main data stream
|
|
124
|
+
|
|
125
|
+
data = read_stream(source_path, stream)
|
|
126
|
+
copied += 1 if data && write_stream(dest_path, stream, data)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
copied
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Get stream information
|
|
133
|
+
#
|
|
134
|
+
# @param file_path [String] Path to file
|
|
135
|
+
# @param stream_name [String] Stream name
|
|
136
|
+
# @return [Hash, nil] Stream info or nil
|
|
137
|
+
def self.stream_info(file_path, stream_name)
|
|
138
|
+
return nil unless available?
|
|
139
|
+
return nil unless File.exist?(file_path)
|
|
140
|
+
|
|
141
|
+
data = read_stream(file_path, stream_name)
|
|
142
|
+
return nil unless data
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
name: stream_name,
|
|
146
|
+
size: data.bytesize,
|
|
147
|
+
exists: true,
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Check if file has any alternate streams
|
|
152
|
+
#
|
|
153
|
+
# @param file_path [String] Path to file
|
|
154
|
+
# @return [Boolean] true if has streams
|
|
155
|
+
def self.has_streams?(file_path)
|
|
156
|
+
return false unless available?
|
|
157
|
+
|
|
158
|
+
streams = list_streams(file_path)
|
|
159
|
+
streams.any? { |s| s != "$DATA" }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Archive streams to a hash
|
|
163
|
+
#
|
|
164
|
+
# @param file_path [String] Path to file
|
|
165
|
+
# @return [Hash<String, String>] Stream name => data
|
|
166
|
+
def self.archive_streams(file_path)
|
|
167
|
+
return {} unless available?
|
|
168
|
+
|
|
169
|
+
streams_data = {}
|
|
170
|
+
streams = list_streams(file_path)
|
|
171
|
+
|
|
172
|
+
streams.each do |stream|
|
|
173
|
+
next if stream == "$DATA"
|
|
174
|
+
|
|
175
|
+
data = read_stream(file_path, stream)
|
|
176
|
+
streams_data[stream] = data if data
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
streams_data
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Restore streams from hash
|
|
183
|
+
#
|
|
184
|
+
# @param file_path [String] Path to file
|
|
185
|
+
# @param streams_data [Hash<String, String>] Stream name => data
|
|
186
|
+
# @return [Integer] Number of streams restored
|
|
187
|
+
def self.restore_streams(file_path, streams_data)
|
|
188
|
+
return 0 unless available?
|
|
189
|
+
return 0 unless File.exist?(file_path)
|
|
190
|
+
|
|
191
|
+
restored = 0
|
|
192
|
+
|
|
193
|
+
streams_data.each do |stream_name, data|
|
|
194
|
+
restored += 1 if write_stream(file_path, stream_name, data)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
restored
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
# Platform detection and capabilities
|
|
5
|
+
# Provides cross-platform compatibility checks
|
|
6
|
+
module Platform
|
|
7
|
+
# Detect if running on Windows
|
|
8
|
+
#
|
|
9
|
+
# @return [Boolean] true if Windows
|
|
10
|
+
def self.windows?
|
|
11
|
+
!!(RUBY_PLATFORM =~ /mswin|mingw|cygwin/)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Detect if running on macOS
|
|
15
|
+
#
|
|
16
|
+
# @return [Boolean] true if macOS
|
|
17
|
+
def self.macos?
|
|
18
|
+
!!RUBY_PLATFORM.include?("darwin")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Detect if running on Linux
|
|
22
|
+
#
|
|
23
|
+
# @return [Boolean] true if Linux
|
|
24
|
+
def self.linux?
|
|
25
|
+
!!RUBY_PLATFORM.include?("linux")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Detect if running on Unix-like system
|
|
29
|
+
#
|
|
30
|
+
# @return [Boolean] true if Unix-like (macOS, Linux, BSD, etc.)
|
|
31
|
+
def self.unix?
|
|
32
|
+
!windows?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Get platform name
|
|
36
|
+
#
|
|
37
|
+
# @return [String] Platform name
|
|
38
|
+
def self.name
|
|
39
|
+
return "Windows" if windows?
|
|
40
|
+
return "macOS" if macos?
|
|
41
|
+
return "Linux" if linux?
|
|
42
|
+
|
|
43
|
+
"Unknown"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Check if NTFS alternate streams are supported
|
|
47
|
+
# Only available on Windows with NTFS filesystem
|
|
48
|
+
#
|
|
49
|
+
# @return [Boolean] true if NTFS streams supported
|
|
50
|
+
def self.supports_ntfs_streams?
|
|
51
|
+
windows?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check if symbolic links are supported
|
|
55
|
+
#
|
|
56
|
+
# @return [Boolean] true if symlinks supported
|
|
57
|
+
def self.supports_symlinks?
|
|
58
|
+
unix? || (windows? && windows_developer_mode?)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Check if hard links are supported
|
|
62
|
+
#
|
|
63
|
+
# @return [Boolean] true if hard links supported
|
|
64
|
+
def self.supports_hardlinks?
|
|
65
|
+
true # Supported on all modern platforms
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Check if extended attributes are supported
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] true if xattrs supported
|
|
71
|
+
def self.supports_extended_attributes?
|
|
72
|
+
unix?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Check if file permissions are supported
|
|
76
|
+
#
|
|
77
|
+
# @return [Boolean] true if POSIX permissions supported
|
|
78
|
+
def self.supports_file_permissions?
|
|
79
|
+
unix?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get platform-specific features
|
|
83
|
+
#
|
|
84
|
+
# @return [Hash] Feature flags
|
|
85
|
+
def self.features
|
|
86
|
+
{
|
|
87
|
+
ntfs_streams: supports_ntfs_streams?,
|
|
88
|
+
symlinks: supports_symlinks?,
|
|
89
|
+
hardlinks: supports_hardlinks?,
|
|
90
|
+
extended_attributes: supports_extended_attributes?,
|
|
91
|
+
file_permissions: supports_file_permissions?,
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Check Windows Developer Mode (for symlink support)
|
|
96
|
+
#
|
|
97
|
+
# @return [Boolean] true if developer mode enabled
|
|
98
|
+
def self.windows_developer_mode?
|
|
99
|
+
return false unless windows?
|
|
100
|
+
|
|
101
|
+
# Check registry for developer mode setting
|
|
102
|
+
# HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock
|
|
103
|
+
# AllowDevelopmentWithoutDevLicense = 1
|
|
104
|
+
begin
|
|
105
|
+
require "win32/registry"
|
|
106
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open(
|
|
107
|
+
'SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock',
|
|
108
|
+
Win32::Registry::KEY_READ,
|
|
109
|
+
) do |reg|
|
|
110
|
+
value = reg["AllowDevelopmentWithoutDevLicense"]
|
|
111
|
+
return value == 1
|
|
112
|
+
end
|
|
113
|
+
rescue LoadError, StandardError
|
|
114
|
+
# win32/registry not available or key doesn't exist
|
|
115
|
+
false
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Get file system type for a path
|
|
120
|
+
#
|
|
121
|
+
# @param path [String] File or directory path
|
|
122
|
+
# @return [String, nil] Filesystem type or nil if unknown
|
|
123
|
+
def self.filesystem_type(path)
|
|
124
|
+
return nil unless File.exist?(path)
|
|
125
|
+
|
|
126
|
+
if windows?
|
|
127
|
+
detect_windows_filesystem(path)
|
|
128
|
+
elsif macos?
|
|
129
|
+
detect_macos_filesystem(path)
|
|
130
|
+
elsif linux?
|
|
131
|
+
detect_linux_filesystem(path)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Check if path is on NTFS filesystem
|
|
136
|
+
#
|
|
137
|
+
# @param path [String] File or directory path
|
|
138
|
+
# @return [Boolean] true if NTFS
|
|
139
|
+
def self.ntfs?(path)
|
|
140
|
+
filesystem_type(path)&.upcase == "NTFS"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Detect Windows filesystem type
|
|
144
|
+
#
|
|
145
|
+
# @param path [String] Path
|
|
146
|
+
# @return [String, nil] Filesystem type
|
|
147
|
+
def self.detect_windows_filesystem(path)
|
|
148
|
+
# Get drive letter
|
|
149
|
+
drive = File.expand_path(path)[0, 2]
|
|
150
|
+
return nil unless drive =~ /^[A-Za-z]:$/
|
|
151
|
+
|
|
152
|
+
# Use fsutil to get filesystem type
|
|
153
|
+
output = `fsutil fsinfo volumeinfo #{drive} 2>&1`
|
|
154
|
+
Regexp.last_match(1) if output =~ /File System Name\s*:\s*(\w+)/i
|
|
155
|
+
rescue StandardError
|
|
156
|
+
nil
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Detect macOS filesystem type
|
|
160
|
+
#
|
|
161
|
+
# @param path [String] Path
|
|
162
|
+
# @return [String, nil] Filesystem type
|
|
163
|
+
def self.detect_macos_filesystem(path)
|
|
164
|
+
output = `df -T #{path} 2>&1`.lines.last
|
|
165
|
+
return nil unless output
|
|
166
|
+
|
|
167
|
+
# Format: filesystem type blocks used avail capacity mounted
|
|
168
|
+
parts = output.split
|
|
169
|
+
parts[1] if parts.size > 1
|
|
170
|
+
rescue StandardError
|
|
171
|
+
nil
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Detect Linux filesystem type
|
|
175
|
+
#
|
|
176
|
+
# @param path [String] Path
|
|
177
|
+
# @return [String, nil] Filesystem type
|
|
178
|
+
def self.detect_linux_filesystem(path)
|
|
179
|
+
output = `df -T #{path} 2>&1`.lines.last
|
|
180
|
+
return nil unless output
|
|
181
|
+
|
|
182
|
+
# Format: filesystem type blocks used avail use% mounted
|
|
183
|
+
parts = output.split
|
|
184
|
+
parts[1] if parts.size > 1
|
|
185
|
+
rescue StandardError
|
|
186
|
+
nil
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|