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,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Metadata
|
|
5
|
+
# Model for archive-level metadata
|
|
6
|
+
class ArchiveMetadata
|
|
7
|
+
attr_reader :archive
|
|
8
|
+
|
|
9
|
+
# Initialize archive metadata
|
|
10
|
+
# @param archive [Omnizip::Zip::File] The archive to manage metadata for
|
|
11
|
+
def initialize(archive)
|
|
12
|
+
@archive = archive
|
|
13
|
+
@modified = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Get archive comment
|
|
17
|
+
# @return [String] Archive comment
|
|
18
|
+
def comment
|
|
19
|
+
archive.comment
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Set archive comment
|
|
23
|
+
# @param value [String] New comment
|
|
24
|
+
def comment=(value)
|
|
25
|
+
archive.comment = value.to_s
|
|
26
|
+
@modified = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get creation date (approximated from first entry)
|
|
30
|
+
# @return [Time, nil] Creation date or nil
|
|
31
|
+
def created_at
|
|
32
|
+
return nil if archive.entries.empty?
|
|
33
|
+
|
|
34
|
+
archive.entries.map(&:time).min
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Get modification date (from newest entry)
|
|
38
|
+
# @return [Time, nil] Modification date or nil
|
|
39
|
+
def modified_at
|
|
40
|
+
return nil if archive.entries.empty?
|
|
41
|
+
|
|
42
|
+
archive.entries.map(&:time).max
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get total uncompressed size
|
|
46
|
+
# @return [Integer] Total uncompressed size in bytes
|
|
47
|
+
def total_size
|
|
48
|
+
archive.entries.sum(&:size)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get total compressed size
|
|
52
|
+
# @return [Integer] Total compressed size in bytes
|
|
53
|
+
def total_compressed_size
|
|
54
|
+
archive.entries.sum(&:compressed_size)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Get compression ratio
|
|
58
|
+
# @return [Float] Compression ratio (0.0 to 1.0)
|
|
59
|
+
def compression_ratio
|
|
60
|
+
return 0.0 if total_size.zero?
|
|
61
|
+
|
|
62
|
+
ratio = 1.0 - (total_compressed_size.to_f / total_size)
|
|
63
|
+
# Clamp ratio between 0.0 and 1.0 (compressed size can exceed
|
|
64
|
+
# original size for small files or incompressible data)
|
|
65
|
+
[[ratio, 0.0].max, 1.0].min
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get entry count
|
|
69
|
+
# @return [Integer] Number of entries
|
|
70
|
+
def entry_count
|
|
71
|
+
archive.entries.size
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Get file count (excluding directories)
|
|
75
|
+
# @return [Integer] Number of files
|
|
76
|
+
def file_count
|
|
77
|
+
archive.entries.count(&:file?)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get directory count
|
|
81
|
+
# @return [Integer] Number of directories
|
|
82
|
+
def directory_count
|
|
83
|
+
archive.entries.count(&:directory?)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Check if metadata has been modified
|
|
87
|
+
# @return [Boolean] True if modified
|
|
88
|
+
def modified?
|
|
89
|
+
@modified
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Reset modified flag
|
|
93
|
+
def reset_modified
|
|
94
|
+
@modified = false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Get all metadata as a hash
|
|
98
|
+
# @return [Hash] Metadata hash
|
|
99
|
+
def to_h
|
|
100
|
+
{
|
|
101
|
+
comment: comment,
|
|
102
|
+
created_at: created_at,
|
|
103
|
+
modified_at: modified_at,
|
|
104
|
+
total_size: total_size,
|
|
105
|
+
total_compressed_size: total_compressed_size,
|
|
106
|
+
compression_ratio: compression_ratio,
|
|
107
|
+
entry_count: entry_count,
|
|
108
|
+
file_count: file_count,
|
|
109
|
+
directory_count: directory_count,
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Metadata
|
|
5
|
+
# Model for file entry metadata
|
|
6
|
+
# Wraps CentralDirectoryHeader with a cleaner metadata API
|
|
7
|
+
class EntryMetadata
|
|
8
|
+
attr_reader :entry
|
|
9
|
+
|
|
10
|
+
# Initialize metadata for an entry
|
|
11
|
+
# @param entry [Omnizip::Zip::Entry] The entry to manage metadata for
|
|
12
|
+
def initialize(entry)
|
|
13
|
+
@entry = entry
|
|
14
|
+
@modified = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get entry comment
|
|
18
|
+
# @return [String] Entry comment
|
|
19
|
+
def comment
|
|
20
|
+
entry.comment
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Set entry comment
|
|
24
|
+
# @param value [String] New comment
|
|
25
|
+
def comment=(value)
|
|
26
|
+
entry.comment = value.to_s
|
|
27
|
+
@modified = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Get modification time
|
|
31
|
+
# @return [Time] Modification time
|
|
32
|
+
def mtime
|
|
33
|
+
entry.time
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Set modification time
|
|
37
|
+
# @param value [Time] New modification time
|
|
38
|
+
def mtime=(value)
|
|
39
|
+
unless value.is_a?(Time)
|
|
40
|
+
raise ArgumentError,
|
|
41
|
+
"mtime must be a Time object"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
entry.header.last_mod_time = dos_time(value)
|
|
45
|
+
entry.header.last_mod_date = dos_date(value)
|
|
46
|
+
@modified = true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Get Unix permissions
|
|
50
|
+
# @return [Integer] Unix permissions (e.g., 0644)
|
|
51
|
+
def unix_permissions
|
|
52
|
+
entry.unix_perms
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Set Unix permissions
|
|
56
|
+
# @param perms [Integer] Unix permissions (e.g., 0644, 0755)
|
|
57
|
+
def unix_permissions=(perms)
|
|
58
|
+
unless perms.is_a?(Integer)
|
|
59
|
+
raise ArgumentError,
|
|
60
|
+
"permissions must be an integer"
|
|
61
|
+
end
|
|
62
|
+
unless (0..0o777).cover?(perms)
|
|
63
|
+
raise ArgumentError,
|
|
64
|
+
"permissions out of range"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
entry.unix_perms = perms
|
|
68
|
+
@modified = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Get external attributes
|
|
72
|
+
# @return [Integer] External attributes
|
|
73
|
+
def attributes
|
|
74
|
+
entry.header.external_attributes
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Set external attributes
|
|
78
|
+
# @param value [Integer, Symbol] External attributes or symbol like :readonly
|
|
79
|
+
def attributes=(value)
|
|
80
|
+
case value
|
|
81
|
+
when Integer
|
|
82
|
+
entry.header.external_attributes = value
|
|
83
|
+
when Symbol
|
|
84
|
+
set_attribute_flag(value)
|
|
85
|
+
else
|
|
86
|
+
raise ArgumentError, "attributes must be Integer or Symbol"
|
|
87
|
+
end
|
|
88
|
+
@modified = true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Check if metadata has been modified
|
|
92
|
+
# @return [Boolean] True if modified
|
|
93
|
+
def modified?
|
|
94
|
+
@modified
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Reset modified flag
|
|
98
|
+
def reset_modified
|
|
99
|
+
@modified = false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Get all metadata as a hash
|
|
103
|
+
# @return [Hash] Metadata hash
|
|
104
|
+
def to_h
|
|
105
|
+
{
|
|
106
|
+
name: entry.name,
|
|
107
|
+
comment: comment,
|
|
108
|
+
mtime: mtime,
|
|
109
|
+
unix_permissions: unix_permissions,
|
|
110
|
+
size: entry.size,
|
|
111
|
+
compressed_size: entry.compressed_size,
|
|
112
|
+
crc: entry.crc,
|
|
113
|
+
directory: entry.directory?,
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
# Set attribute flag
|
|
120
|
+
def set_attribute_flag(flag)
|
|
121
|
+
case flag
|
|
122
|
+
when :readonly
|
|
123
|
+
entry.header.external_attributes |= 0x01
|
|
124
|
+
when :hidden
|
|
125
|
+
entry.header.external_attributes |= 0x02
|
|
126
|
+
when :system
|
|
127
|
+
entry.header.external_attributes |= 0x04
|
|
128
|
+
when :archive
|
|
129
|
+
entry.header.external_attributes |= 0x20
|
|
130
|
+
else
|
|
131
|
+
raise ArgumentError, "Unknown attribute flag: #{flag}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Convert Time to DOS time format
|
|
136
|
+
def dos_time(time)
|
|
137
|
+
((time.hour << 11) | (time.min << 5) | (time.sec / 2)) & 0xFFFF
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Convert Time to DOS date format
|
|
141
|
+
def dos_date(time)
|
|
142
|
+
(((time.year - 1980) << 9) | (time.month << 5) | time.day) & 0xFFFF
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "metadata_validator"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Metadata
|
|
7
|
+
# Coordinates metadata editing operations
|
|
8
|
+
class MetadataEditor
|
|
9
|
+
attr_reader :archive, :validator
|
|
10
|
+
|
|
11
|
+
# Initialize metadata editor
|
|
12
|
+
# @param archive [Omnizip::Zip::File] Archive to edit
|
|
13
|
+
def initialize(archive)
|
|
14
|
+
@archive = archive
|
|
15
|
+
@validator = MetadataValidator.new
|
|
16
|
+
@changes = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Set timestamps for all entries
|
|
20
|
+
# @param time [Time] Time to set
|
|
21
|
+
# @param filter [Proc] Optional filter to select entries
|
|
22
|
+
def set_all_timestamps(time, &filter)
|
|
23
|
+
validator.validate_time(time)
|
|
24
|
+
|
|
25
|
+
entries = filter ? archive.entries.select(&filter) : archive.entries
|
|
26
|
+
|
|
27
|
+
entries.each do |entry|
|
|
28
|
+
next if entry.directory?
|
|
29
|
+
|
|
30
|
+
metadata = EntryMetadata.new(entry)
|
|
31
|
+
metadata.mtime = time
|
|
32
|
+
@changes << { entry: entry, field: :mtime, value: time }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Normalize permissions (set sensible defaults)
|
|
39
|
+
# @param file_perms [Integer] Permissions for files (default: 0644)
|
|
40
|
+
# @param dir_perms [Integer] Permissions for directories (default: 0755)
|
|
41
|
+
def normalize_permissions(file_perms: 0o644, dir_perms: 0o755)
|
|
42
|
+
validator.validate_permissions(file_perms)
|
|
43
|
+
validator.validate_permissions(dir_perms)
|
|
44
|
+
|
|
45
|
+
archive.entries.each do |entry|
|
|
46
|
+
metadata = EntryMetadata.new(entry)
|
|
47
|
+
perms = entry.directory? ? dir_perms : file_perms
|
|
48
|
+
metadata.unix_permissions = perms
|
|
49
|
+
@changes << { entry: entry, field: :permissions, value: perms }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Strip all comments from entries and archive
|
|
56
|
+
def strip_comments
|
|
57
|
+
archive.entries.each do |entry|
|
|
58
|
+
metadata = EntryMetadata.new(entry)
|
|
59
|
+
metadata.comment = ""
|
|
60
|
+
@changes << { entry: entry, field: :comment, value: "" }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
archive.comment = ""
|
|
64
|
+
@changes << { entry: :archive, field: :comment, value: "" }
|
|
65
|
+
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Set comment for entries matching a pattern
|
|
70
|
+
# @param pattern [String, Regexp] Pattern to match
|
|
71
|
+
# @param comment [String] Comment to set
|
|
72
|
+
def set_comment_matching(pattern, comment)
|
|
73
|
+
validator.validate_comment(comment)
|
|
74
|
+
|
|
75
|
+
matching_entries = if pattern.is_a?(Regexp)
|
|
76
|
+
archive.entries.select { |e| e.name =~ pattern }
|
|
77
|
+
else
|
|
78
|
+
archive.entries.select do |e|
|
|
79
|
+
::File.fnmatch(pattern, e.name)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
matching_entries.each do |entry|
|
|
84
|
+
metadata = EntryMetadata.new(entry)
|
|
85
|
+
metadata.comment = comment
|
|
86
|
+
@changes << { entry: entry, field: :comment, value: comment }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Set permissions for entries matching a pattern
|
|
93
|
+
# @param pattern [String, Regexp] Pattern to match
|
|
94
|
+
# @param perms [Integer] Permissions to set
|
|
95
|
+
def set_permissions_matching(pattern, perms)
|
|
96
|
+
validator.validate_permissions(perms)
|
|
97
|
+
|
|
98
|
+
matching_entries = if pattern.is_a?(Regexp)
|
|
99
|
+
archive.entries.select { |e| e.name =~ pattern }
|
|
100
|
+
else
|
|
101
|
+
archive.entries.select do |e|
|
|
102
|
+
::File.fnmatch(pattern, e.name)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
matching_entries.each do |entry|
|
|
107
|
+
next if entry.directory?
|
|
108
|
+
|
|
109
|
+
metadata = EntryMetadata.new(entry)
|
|
110
|
+
metadata.unix_permissions = perms
|
|
111
|
+
@changes << { entry: entry, field: :permissions, value: perms }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
self
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Update timestamps to preserve relative ordering
|
|
118
|
+
# Useful for maintaining build timestamps while updating absolute times
|
|
119
|
+
# @param base_time [Time] Base time for earliest file
|
|
120
|
+
def preserve_relative_timestamps(base_time = Time.now)
|
|
121
|
+
validator.validate_time(base_time)
|
|
122
|
+
|
|
123
|
+
# Find earliest timestamp
|
|
124
|
+
earliest = archive.entries.map(&:time).min
|
|
125
|
+
return self unless earliest
|
|
126
|
+
|
|
127
|
+
# Calculate offset
|
|
128
|
+
offset = base_time.to_i - earliest.to_i
|
|
129
|
+
|
|
130
|
+
archive.entries.each do |entry|
|
|
131
|
+
next if entry.directory?
|
|
132
|
+
|
|
133
|
+
new_time = Time.at(entry.time.to_i + offset)
|
|
134
|
+
metadata = EntryMetadata.new(entry)
|
|
135
|
+
metadata.mtime = new_time
|
|
136
|
+
@changes << { entry: entry, field: :mtime, value: new_time }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
self
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Get list of pending changes
|
|
143
|
+
# @return [Array<Hash>] List of changes
|
|
144
|
+
def pending_changes
|
|
145
|
+
@changes
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Check if there are pending changes
|
|
149
|
+
# @return [Boolean] True if there are changes
|
|
150
|
+
def modified?
|
|
151
|
+
!@changes.empty?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Clear pending changes
|
|
155
|
+
def clear_changes
|
|
156
|
+
@changes = []
|
|
157
|
+
self
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Commit all changes to archive
|
|
161
|
+
# This marks the archive as modified and saves on close
|
|
162
|
+
def commit
|
|
163
|
+
# Changes are already applied to entries
|
|
164
|
+
# Just mark archive as modified
|
|
165
|
+
archive.instance_variable_set(:@modified, true) if modified?
|
|
166
|
+
@changes = []
|
|
167
|
+
self
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Metadata
|
|
5
|
+
# Registry of supported metadata per archive format
|
|
6
|
+
class MetadataRegistry
|
|
7
|
+
@registry = {}
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
# Register metadata support for a format
|
|
11
|
+
# @param format [Symbol] Format name (e.g., :zip, :seven_zip)
|
|
12
|
+
# @param supported_fields [Array<Symbol>] Supported metadata fields
|
|
13
|
+
def register(format, supported_fields)
|
|
14
|
+
@registry[format] = supported_fields
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Check if a format supports a metadata field
|
|
18
|
+
# @param format [Symbol] Format name
|
|
19
|
+
# @param field [Symbol] Metadata field name
|
|
20
|
+
# @return [Boolean] True if supported
|
|
21
|
+
def supports?(format, field)
|
|
22
|
+
fields = @registry[format]
|
|
23
|
+
return false unless fields
|
|
24
|
+
|
|
25
|
+
fields.include?(field)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Get all supported fields for a format
|
|
29
|
+
# @param format [Symbol] Format name
|
|
30
|
+
# @return [Array<Symbol>] List of supported fields
|
|
31
|
+
def supported_fields(format)
|
|
32
|
+
@registry[format] || []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Get all registered formats
|
|
36
|
+
# @return [Array<Symbol>] List of formats
|
|
37
|
+
def formats
|
|
38
|
+
@registry.keys
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Reset registry (for testing)
|
|
42
|
+
def reset
|
|
43
|
+
@registry = {}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Register ZIP format metadata support
|
|
49
|
+
MetadataRegistry.register(:zip, %i[
|
|
50
|
+
comment
|
|
51
|
+
mtime
|
|
52
|
+
unix_permissions
|
|
53
|
+
external_attributes
|
|
54
|
+
filename
|
|
55
|
+
extra_field
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
# Register 7z format metadata support (limited)
|
|
59
|
+
MetadataRegistry.register(:seven_zip, %i[
|
|
60
|
+
mtime
|
|
61
|
+
attributes
|
|
62
|
+
])
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Metadata
|
|
5
|
+
# Validates metadata changes before applying
|
|
6
|
+
class MetadataValidator
|
|
7
|
+
# Validate entry metadata
|
|
8
|
+
# @param metadata [EntryMetadata] Metadata to validate
|
|
9
|
+
# @raise [ArgumentError] If metadata is invalid
|
|
10
|
+
def validate_entry(metadata)
|
|
11
|
+
validate_comment(metadata.comment) if metadata.comment
|
|
12
|
+
validate_time(metadata.mtime) if metadata.respond_to?(:mtime)
|
|
13
|
+
validate_permissions(metadata.unix_permissions) if metadata.unix_permissions
|
|
14
|
+
|
|
15
|
+
true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Validate archive metadata
|
|
19
|
+
# @param metadata [ArchiveMetadata] Metadata to validate
|
|
20
|
+
# @raise [ArgumentError] If metadata is invalid
|
|
21
|
+
def validate_archive(metadata)
|
|
22
|
+
validate_comment(metadata.comment) if metadata.comment
|
|
23
|
+
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Validate comment length
|
|
28
|
+
# @param comment [String] Comment to validate
|
|
29
|
+
# @raise [ArgumentError] If comment is too long
|
|
30
|
+
def validate_comment(comment)
|
|
31
|
+
max_length = 65_535 # ZIP format limit
|
|
32
|
+
return true if comment.bytesize <= max_length
|
|
33
|
+
|
|
34
|
+
raise ArgumentError,
|
|
35
|
+
"Comment too long: #{comment.bytesize} bytes (max: #{max_length})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Validate time value
|
|
39
|
+
# @param time [Time] Time to validate
|
|
40
|
+
# @raise [ArgumentError] If time is invalid
|
|
41
|
+
def validate_time(time)
|
|
42
|
+
return true unless time
|
|
43
|
+
|
|
44
|
+
unless time.is_a?(Time)
|
|
45
|
+
raise ArgumentError, "Time must be a Time object, got #{time.class}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# DOS time range: 1980-01-01 to 2107-12-31
|
|
49
|
+
min_time = Time.new(1980, 1, 1)
|
|
50
|
+
max_time = Time.new(2107, 12, 31, 23, 59, 59)
|
|
51
|
+
|
|
52
|
+
if time < min_time || time > max_time
|
|
53
|
+
raise ArgumentError, "Time out of DOS range (1980-2107): #{time}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Validate Unix permissions
|
|
60
|
+
# @param perms [Integer] Permissions to validate
|
|
61
|
+
# @raise [ArgumentError] If permissions are invalid
|
|
62
|
+
def validate_permissions(perms)
|
|
63
|
+
return true unless perms
|
|
64
|
+
|
|
65
|
+
unless perms.is_a?(Integer)
|
|
66
|
+
raise ArgumentError,
|
|
67
|
+
"Permissions must be an integer, got #{perms.class}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
unless (0..0o777).cover?(perms)
|
|
71
|
+
raise ArgumentError,
|
|
72
|
+
"Permissions out of range: 0#{perms.to_s(8)} (max: 0777)"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Validate filename
|
|
79
|
+
# @param filename [String] Filename to validate
|
|
80
|
+
# @raise [ArgumentError] If filename is invalid
|
|
81
|
+
def validate_filename(filename)
|
|
82
|
+
return true unless filename
|
|
83
|
+
|
|
84
|
+
max_length = 65_535 # ZIP format limit
|
|
85
|
+
if filename.bytesize > max_length
|
|
86
|
+
raise ArgumentError,
|
|
87
|
+
"Filename too long: #{filename.bytesize} bytes (max: #{max_length})"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check for invalid characters
|
|
91
|
+
if filename.include?("\x00")
|
|
92
|
+
raise ArgumentError, "Filename contains null character"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
true
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "metadata/entry_metadata"
|
|
4
|
+
require_relative "metadata/archive_metadata"
|
|
5
|
+
require_relative "metadata/metadata_validator"
|
|
6
|
+
require_relative "metadata/metadata_registry"
|
|
7
|
+
require_relative "metadata/metadata_editor"
|
|
8
|
+
|
|
9
|
+
module Omnizip
|
|
10
|
+
# Metadata editing module
|
|
11
|
+
# Provides in-place metadata modification without recompression
|
|
12
|
+
module Metadata
|
|
13
|
+
class << self
|
|
14
|
+
# Edit entry metadata
|
|
15
|
+
# @param entry [Omnizip::Zip::Entry] Entry to edit
|
|
16
|
+
# @yield [metadata] Block to modify metadata
|
|
17
|
+
# @return [EntryMetadata] Entry metadata object
|
|
18
|
+
def edit_entry(entry, &block)
|
|
19
|
+
metadata = EntryMetadata.new(entry)
|
|
20
|
+
yield(metadata) if block
|
|
21
|
+
metadata
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Edit archive metadata
|
|
25
|
+
# @param archive [Omnizip::Zip::File] Archive to edit
|
|
26
|
+
# @yield [metadata] Block to modify metadata
|
|
27
|
+
# @return [ArchiveMetadata] Archive metadata object
|
|
28
|
+
def edit_archive(archive, &block)
|
|
29
|
+
metadata = ArchiveMetadata.new(archive)
|
|
30
|
+
yield(metadata) if block
|
|
31
|
+
metadata
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Create a metadata editor for batch operations
|
|
35
|
+
# @param archive [Omnizip::Zip::File] Archive to edit
|
|
36
|
+
# @return [MetadataEditor] Metadata editor
|
|
37
|
+
def editor(archive)
|
|
38
|
+
MetadataEditor.new(archive)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Validate metadata
|
|
42
|
+
# @param metadata [EntryMetadata, ArchiveMetadata] Metadata to validate
|
|
43
|
+
# @return [Boolean] True if valid
|
|
44
|
+
def validate(metadata)
|
|
45
|
+
validator = MetadataValidator.new
|
|
46
|
+
case metadata
|
|
47
|
+
when EntryMetadata
|
|
48
|
+
validator.validate_entry(metadata)
|
|
49
|
+
when ArchiveMetadata
|
|
50
|
+
validator.validate_archive(metadata)
|
|
51
|
+
else
|
|
52
|
+
raise ArgumentError, "Unknown metadata type: #{metadata.class}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
File without changes
|