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.
Files changed (511) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +754 -0
  5. data/COPYING +502 -0
  6. data/Gemfile +17 -0
  7. data/LICENSE +12 -0
  8. data/README.adoc +1045 -0
  9. data/Rakefile +12 -0
  10. data/benchmark/README.md +260 -0
  11. data/benchmark/benchmark_suite.rb +125 -0
  12. data/benchmark/compression_bench.rb +181 -0
  13. data/benchmark/filter_bench.rb +180 -0
  14. data/benchmark/models/benchmark_result.rb +59 -0
  15. data/benchmark/models/comparison_result.rb +69 -0
  16. data/benchmark/profile_suite.rb +167 -0
  17. data/benchmark/reporter.rb +150 -0
  18. data/benchmark/run_benchmarks.rb +66 -0
  19. data/benchmark/test_data.rb +137 -0
  20. data/config/formats/rar3_spec.yml +91 -0
  21. data/config/formats/rar5_spec.yml +102 -0
  22. data/docs/.github/workflows/docs.yml +142 -0
  23. data/docs/.gitignore +21 -0
  24. data/docs/.lychee.toml +67 -0
  25. data/docs/Gemfile +13 -0
  26. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  27. data/docs/README.md +101 -0
  28. data/docs/_config.yml +112 -0
  29. data/docs/assets/logo.svg +1 -0
  30. data/docs/assets/omnizip-logo.pdf +1540 -11
  31. data/docs/comparison/feature-matrix.adoc +694 -0
  32. data/docs/comparison/index.adoc +113 -0
  33. data/docs/comparison/vs-7zip.adoc +309 -0
  34. data/docs/comparison/vs-peazip.adoc +77 -0
  35. data/docs/comparison/vs-rubyzip.adoc +342 -0
  36. data/docs/comparison/vs-winrar.adoc +100 -0
  37. data/docs/compatibility.adoc +579 -0
  38. data/docs/concepts/index.adoc +129 -0
  39. data/docs/developer/architecture.adoc +256 -0
  40. data/docs/developer/contributing.adoc +158 -0
  41. data/docs/developer/index.adoc +25 -0
  42. data/docs/developer/testing.adoc +212 -0
  43. data/docs/getting-started/basic-usage.adoc +271 -0
  44. data/docs/getting-started/index.adoc +42 -0
  45. data/docs/getting-started/installation.adoc +138 -0
  46. data/docs/getting-started/quick-start.adoc +185 -0
  47. data/docs/getting-started/your-first-archive.adoc +218 -0
  48. data/docs/guides/advanced-features/encryption.adoc +300 -0
  49. data/docs/guides/advanced-features/index.adoc +49 -0
  50. data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
  51. data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
  52. data/docs/guides/advanced-features/streaming.adoc +212 -0
  53. data/docs/guides/archive-formats/gzip-format.adoc +107 -0
  54. data/docs/guides/archive-formats/index.adoc +130 -0
  55. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  56. data/docs/guides/archive-formats/rar5.adoc +521 -0
  57. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  58. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  59. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  60. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  61. data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
  62. data/docs/guides/compression-algorithms/deflate.adoc +319 -0
  63. data/docs/guides/compression-algorithms/index.adoc +190 -0
  64. data/docs/guides/compression-algorithms/lzma.adoc +398 -0
  65. data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
  66. data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
  67. data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
  68. data/docs/guides/creating-archives.adoc +354 -0
  69. data/docs/guides/extracting-archives.adoc +53 -0
  70. data/docs/guides/format-conversion.adoc +64 -0
  71. data/docs/guides/index.adoc +49 -0
  72. data/docs/guides/migration-rubyzip.adoc +217 -0
  73. data/docs/guides/parity-archives.adoc +605 -0
  74. data/docs/guides/performance-tuning.adoc +88 -0
  75. data/docs/index.adoc +218 -0
  76. data/docs/lychee.toml +67 -0
  77. data/docs/reference/api/overview.adoc +188 -0
  78. data/docs/reference/cli/compress-command.adoc +114 -0
  79. data/docs/reference/cli/overview.adoc +140 -0
  80. data/docs/reference/index.adoc +26 -0
  81. data/docs/resources/faq.adoc +185 -0
  82. data/docs/resources/quick-reference.adoc +222 -0
  83. data/docs/troubleshooting/index.adoc +208 -0
  84. data/examples/api_comparison.rb +205 -0
  85. data/examples/deflate64_example.rb +96 -0
  86. data/examples/par2_demo.rb +121 -0
  87. data/examples/quick_start_native.rb +150 -0
  88. data/examples/quick_start_rubyzip.rb +115 -0
  89. data/examples/rubyzip_compatibility_demo.rb +194 -0
  90. data/exe/omnizip +27 -0
  91. data/lib/omnizip/algorithm.rb +130 -0
  92. data/lib/omnizip/algorithm_registry.rb +86 -0
  93. data/lib/omnizip/algorithms/.keep +0 -0
  94. data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
  95. data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
  96. data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
  97. data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
  98. data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
  99. data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
  100. data/lib/omnizip/algorithms/bzip2.rb +130 -0
  101. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  102. data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
  103. data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
  104. data/lib/omnizip/algorithms/deflate.rb +128 -0
  105. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  106. data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
  107. data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
  108. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
  109. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
  110. data/lib/omnizip/algorithms/deflate64.rb +109 -0
  111. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  112. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  113. data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
  114. data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
  115. data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
  116. data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
  117. data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
  118. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
  119. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
  120. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
  121. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
  122. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  123. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  124. data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
  125. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  126. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
  127. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
  128. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  129. data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
  130. data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
  131. data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
  132. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  133. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
  134. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
  135. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
  136. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
  137. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
  138. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
  139. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  140. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
  141. data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
  142. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
  143. data/lib/omnizip/algorithms/lzma.rb +238 -0
  144. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
  145. data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
  146. data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
  147. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
  148. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  149. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
  150. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  151. data/lib/omnizip/algorithms/lzma2.rb +141 -0
  152. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  153. data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
  154. data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
  155. data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
  156. data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
  157. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  158. data/lib/omnizip/algorithms/ppmd7.rb +116 -0
  159. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  160. data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
  161. data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
  162. data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
  163. data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
  164. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  165. data/lib/omnizip/algorithms/ppmd8.rb +82 -0
  166. data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
  167. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
  168. data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
  169. data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
  170. data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
  171. data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
  172. data/lib/omnizip/algorithms/zstandard.rb +138 -0
  173. data/lib/omnizip/buffer/memory_archive.rb +251 -0
  174. data/lib/omnizip/buffer/memory_extractor.rb +224 -0
  175. data/lib/omnizip/buffer.rb +176 -0
  176. data/lib/omnizip/checksum_registry.rb +114 -0
  177. data/lib/omnizip/checksums/crc32.rb +100 -0
  178. data/lib/omnizip/checksums/crc64.rb +101 -0
  179. data/lib/omnizip/checksums/crc_base.rb +158 -0
  180. data/lib/omnizip/checksums/verifier.rb +131 -0
  181. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  182. data/lib/omnizip/chunked/reader.rb +78 -0
  183. data/lib/omnizip/chunked/writer.rb +120 -0
  184. data/lib/omnizip/chunked.rb +129 -0
  185. data/lib/omnizip/cli/output_formatter.rb +104 -0
  186. data/lib/omnizip/cli.rb +572 -0
  187. data/lib/omnizip/commands/.keep +0 -0
  188. data/lib/omnizip/commands/archive_create_command.rb +427 -0
  189. data/lib/omnizip/commands/archive_extract_command.rb +272 -0
  190. data/lib/omnizip/commands/archive_list_command.rb +218 -0
  191. data/lib/omnizip/commands/archive_repair_command.rb +131 -0
  192. data/lib/omnizip/commands/archive_verify_command.rb +117 -0
  193. data/lib/omnizip/commands/compress_command.rb +117 -0
  194. data/lib/omnizip/commands/decompress_command.rb +120 -0
  195. data/lib/omnizip/commands/list_command.rb +53 -0
  196. data/lib/omnizip/commands/metadata_command.rb +153 -0
  197. data/lib/omnizip/commands/parity_create_command.rb +122 -0
  198. data/lib/omnizip/commands/parity_repair_command.rb +122 -0
  199. data/lib/omnizip/commands/parity_verify_command.rb +124 -0
  200. data/lib/omnizip/commands/profile_list_command.rb +56 -0
  201. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  202. data/lib/omnizip/convenience.rb +359 -0
  203. data/lib/omnizip/converter/conversion_registry.rb +49 -0
  204. data/lib/omnizip/converter/conversion_strategy.rb +121 -0
  205. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
  206. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
  207. data/lib/omnizip/converter.rb +105 -0
  208. data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
  209. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  210. data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
  211. data/lib/omnizip/crypto/aes256.rb +102 -0
  212. data/lib/omnizip/error.rb +106 -0
  213. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
  214. data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
  215. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  216. data/lib/omnizip/eta/sample_history.rb +143 -0
  217. data/lib/omnizip/eta/time_estimator.rb +106 -0
  218. data/lib/omnizip/eta.rb +63 -0
  219. data/lib/omnizip/extraction/filter_chain.rb +177 -0
  220. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  221. data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
  222. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  223. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  224. data/lib/omnizip/extraction/selective_extractor.rb +240 -0
  225. data/lib/omnizip/extraction.rb +111 -0
  226. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  227. data/lib/omnizip/file_type.rb +113 -0
  228. data/lib/omnizip/filter.rb +139 -0
  229. data/lib/omnizip/filter_pipeline.rb +108 -0
  230. data/lib/omnizip/filter_registry.rb +166 -0
  231. data/lib/omnizip/filters/bcj.rb +279 -0
  232. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  233. data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
  234. data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
  235. data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
  236. data/lib/omnizip/filters/bcj2.rb +99 -0
  237. data/lib/omnizip/filters/bcj_arm.rb +176 -0
  238. data/lib/omnizip/filters/bcj_arm64.rb +244 -0
  239. data/lib/omnizip/filters/bcj_ia64.rb +196 -0
  240. data/lib/omnizip/filters/bcj_ppc.rb +190 -0
  241. data/lib/omnizip/filters/bcj_sparc.rb +176 -0
  242. data/lib/omnizip/filters/bcj_x86.rb +193 -0
  243. data/lib/omnizip/filters/delta.rb +196 -0
  244. data/lib/omnizip/filters/filter_base.rb +72 -0
  245. data/lib/omnizip/filters/registry.rb +123 -0
  246. data/lib/omnizip/filters/xz_delta.rb +258 -0
  247. data/lib/omnizip/format_detector.rb +162 -0
  248. data/lib/omnizip/format_registry.rb +59 -0
  249. data/lib/omnizip/formats/.keep +0 -0
  250. data/lib/omnizip/formats/bzip2_file.rb +172 -0
  251. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  252. data/lib/omnizip/formats/cpio/entry.rb +385 -0
  253. data/lib/omnizip/formats/cpio/reader.rb +196 -0
  254. data/lib/omnizip/formats/cpio/writer.rb +234 -0
  255. data/lib/omnizip/formats/cpio.rb +140 -0
  256. data/lib/omnizip/formats/format_spec_loader.rb +230 -0
  257. data/lib/omnizip/formats/gzip.rb +238 -0
  258. data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
  259. data/lib/omnizip/formats/iso/directory_record.rb +152 -0
  260. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  261. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  262. data/lib/omnizip/formats/iso/reader.rb +197 -0
  263. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  264. data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
  265. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  266. data/lib/omnizip/formats/iso/writer.rb +530 -0
  267. data/lib/omnizip/formats/iso.rb +140 -0
  268. data/lib/omnizip/formats/lzip.rb +175 -0
  269. data/lib/omnizip/formats/lzma_alone.rb +171 -0
  270. data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
  271. data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
  272. data/lib/omnizip/formats/rar/block_parser.rb +243 -0
  273. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  274. data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
  275. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
  276. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
  277. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  278. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  279. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  280. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  281. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
  282. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
  283. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
  284. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  285. data/lib/omnizip/formats/rar/constants.rb +82 -0
  286. data/lib/omnizip/formats/rar/decompressor.rb +238 -0
  287. data/lib/omnizip/formats/rar/external_writer.rb +312 -0
  288. data/lib/omnizip/formats/rar/header.rb +192 -0
  289. data/lib/omnizip/formats/rar/license_validator.rb +109 -0
  290. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  291. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  292. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  293. data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
  294. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
  295. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
  296. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  297. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  298. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
  299. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  300. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
  301. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  302. data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
  303. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  304. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  305. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  306. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  307. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  308. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
  309. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  310. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
  311. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
  312. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
  313. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  314. data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
  315. data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
  316. data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
  317. data/lib/omnizip/formats/rar/reader.rb +366 -0
  318. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  319. data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
  320. data/lib/omnizip/formats/rar/writer.rb +431 -0
  321. data/lib/omnizip/formats/rar.rb +205 -0
  322. data/lib/omnizip/formats/rar3/compressor.rb +73 -0
  323. data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
  324. data/lib/omnizip/formats/rar3/reader.rb +386 -0
  325. data/lib/omnizip/formats/rar3/writer.rb +219 -0
  326. data/lib/omnizip/formats/rar5/compressor.rb +73 -0
  327. data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
  328. data/lib/omnizip/formats/rar5/reader.rb +342 -0
  329. data/lib/omnizip/formats/rar5/writer.rb +214 -0
  330. data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
  331. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  332. data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
  333. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
  334. data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
  335. data/lib/omnizip/formats/seven_zip/header.rb +106 -0
  336. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
  337. data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
  338. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  339. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
  340. data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
  341. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  342. data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
  343. data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
  344. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
  345. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
  346. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  347. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
  348. data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
  349. data/lib/omnizip/formats/seven_zip.rb +93 -0
  350. data/lib/omnizip/formats/tar/constants.rb +73 -0
  351. data/lib/omnizip/formats/tar/entry.rb +94 -0
  352. data/lib/omnizip/formats/tar/header.rb +168 -0
  353. data/lib/omnizip/formats/tar/reader.rb +121 -0
  354. data/lib/omnizip/formats/tar/writer.rb +216 -0
  355. data/lib/omnizip/formats/tar.rb +84 -0
  356. data/lib/omnizip/formats/xz/reader.rb +116 -0
  357. data/lib/omnizip/formats/xz.rb +237 -0
  358. data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
  359. data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
  360. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  361. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
  362. data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
  363. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  364. data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
  365. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
  366. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
  367. data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
  368. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
  369. data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
  370. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
  371. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  372. data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
  373. data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
  374. data/lib/omnizip/formats/zip/constants.rb +69 -0
  375. data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
  376. data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
  377. data/lib/omnizip/formats/zip/reader.rb +250 -0
  378. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  379. data/lib/omnizip/formats/zip/writer.rb +375 -0
  380. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
  381. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
  382. data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
  383. data/lib/omnizip/formats/zip.rb +50 -0
  384. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  385. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  386. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  387. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  388. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  389. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
  390. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
  391. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
  392. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
  393. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
  394. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
  395. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
  396. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
  397. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
  398. data/lib/omnizip/io/buffered_input.rb +146 -0
  399. data/lib/omnizip/io/buffered_output.rb +105 -0
  400. data/lib/omnizip/io/stream_manager.rb +115 -0
  401. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  402. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  403. data/lib/omnizip/link_handler.rb +124 -0
  404. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  405. data/lib/omnizip/metadata/entry_metadata.rb +146 -0
  406. data/lib/omnizip/metadata/metadata_editor.rb +171 -0
  407. data/lib/omnizip/metadata/metadata_registry.rb +64 -0
  408. data/lib/omnizip/metadata/metadata_validator.rb +99 -0
  409. data/lib/omnizip/metadata.rb +57 -0
  410. data/lib/omnizip/models/.keep +0 -0
  411. data/lib/omnizip/models/algorithm_metadata.rb +73 -0
  412. data/lib/omnizip/models/compression_options.rb +71 -0
  413. data/lib/omnizip/models/conversion_options.rb +87 -0
  414. data/lib/omnizip/models/conversion_result.rb +135 -0
  415. data/lib/omnizip/models/eta_result.rb +46 -0
  416. data/lib/omnizip/models/extraction_rule.rb +115 -0
  417. data/lib/omnizip/models/filter_chain.rb +144 -0
  418. data/lib/omnizip/models/filter_config.rb +183 -0
  419. data/lib/omnizip/models/match_result.rb +124 -0
  420. data/lib/omnizip/models/optimization_suggestion.rb +91 -0
  421. data/lib/omnizip/models/parallel_options.rb +104 -0
  422. data/lib/omnizip/models/performance_result.rb +79 -0
  423. data/lib/omnizip/models/profile_report.rb +82 -0
  424. data/lib/omnizip/models/progress_options.rb +38 -0
  425. data/lib/omnizip/models/split_options.rb +116 -0
  426. data/lib/omnizip/optimization_registry.rb +81 -0
  427. data/lib/omnizip/parallel/job_queue.rb +209 -0
  428. data/lib/omnizip/parallel/job_scheduler.rb +203 -0
  429. data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
  430. data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
  431. data/lib/omnizip/parallel/worker_pool.rb +223 -0
  432. data/lib/omnizip/parallel.rb +149 -0
  433. data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
  434. data/lib/omnizip/parity/galois16.rb +145 -0
  435. data/lib/omnizip/parity/models/creator_packet.rb +73 -0
  436. data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
  437. data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
  438. data/lib/omnizip/parity/models/main_packet.rb +128 -0
  439. data/lib/omnizip/parity/models/packet.rb +156 -0
  440. data/lib/omnizip/parity/models/packet_registry.rb +109 -0
  441. data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
  442. data/lib/omnizip/parity/par2_creator.rb +531 -0
  443. data/lib/omnizip/parity/par2_repairer.rb +407 -0
  444. data/lib/omnizip/parity/par2_verifier.rb +364 -0
  445. data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
  446. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  447. data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
  448. data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
  449. data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
  450. data/lib/omnizip/parity.rb +186 -0
  451. data/lib/omnizip/password/encryption_registry.rb +65 -0
  452. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  453. data/lib/omnizip/password/password_validator.rb +129 -0
  454. data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
  455. data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
  456. data/lib/omnizip/password.rb +87 -0
  457. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  458. data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
  459. data/lib/omnizip/pipe.rb +121 -0
  460. data/lib/omnizip/platform/ntfs_streams.rb +201 -0
  461. data/lib/omnizip/platform.rb +189 -0
  462. data/lib/omnizip/profile/archive_profile.rb +39 -0
  463. data/lib/omnizip/profile/balanced_profile.rb +33 -0
  464. data/lib/omnizip/profile/binary_profile.rb +36 -0
  465. data/lib/omnizip/profile/compression_profile.rb +158 -0
  466. data/lib/omnizip/profile/custom_profile.rb +157 -0
  467. data/lib/omnizip/profile/fast_profile.rb +33 -0
  468. data/lib/omnizip/profile/maximum_profile.rb +33 -0
  469. data/lib/omnizip/profile/profile_detector.rb +110 -0
  470. data/lib/omnizip/profile/profile_registry.rb +161 -0
  471. data/lib/omnizip/profile/text_profile.rb +36 -0
  472. data/lib/omnizip/profile.rb +190 -0
  473. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  474. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  475. data/lib/omnizip/profiler/report_generator.rb +169 -0
  476. data/lib/omnizip/profiler.rb +204 -0
  477. data/lib/omnizip/progress/callback_reporter.rb +36 -0
  478. data/lib/omnizip/progress/console_reporter.rb +62 -0
  479. data/lib/omnizip/progress/log_reporter.rb +91 -0
  480. data/lib/omnizip/progress/operation_progress.rb +118 -0
  481. data/lib/omnizip/progress/progress_bar.rb +156 -0
  482. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  483. data/lib/omnizip/progress/progress_tracker.rb +190 -0
  484. data/lib/omnizip/progress/silent_reporter.rb +24 -0
  485. data/lib/omnizip/progress.rb +127 -0
  486. data/lib/omnizip/rubyzip_compat.rb +63 -0
  487. data/lib/omnizip/temp/safe_extract.rb +168 -0
  488. data/lib/omnizip/temp/temp_file.rb +124 -0
  489. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  490. data/lib/omnizip/temp.rb +181 -0
  491. data/lib/omnizip/version.rb +5 -0
  492. data/lib/omnizip/zip/entry.rb +156 -0
  493. data/lib/omnizip/zip/file.rb +485 -0
  494. data/lib/omnizip/zip/input_stream.rb +273 -0
  495. data/lib/omnizip/zip/output_stream.rb +324 -0
  496. data/lib/omnizip.rb +156 -0
  497. data/readme-docs/advanced-features.adoc +515 -0
  498. data/readme-docs/api-usage.adoc +444 -0
  499. data/readme-docs/architecture.adoc +449 -0
  500. data/readme-docs/archive-formats.adoc +479 -0
  501. data/readme-docs/cli-usage.adoc +222 -0
  502. data/readme-docs/compression-algorithms.adoc +442 -0
  503. data/readme-docs/compression-profiles.adoc +247 -0
  504. data/readme-docs/encryption-checksums.adoc +328 -0
  505. data/readme-docs/format-converter.adoc +325 -0
  506. data/readme-docs/installation.adoc +228 -0
  507. data/readme-docs/par2-archives.adoc +608 -0
  508. data/readme-docs/performance-profiler.adoc +389 -0
  509. data/readme-docs/preprocessing-filters.adoc +280 -0
  510. data/xz-file-format-1.2.1.txt +1174 -0
  511. 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