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,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+
5
+ module Omnizip
6
+ module Formats
7
+ module Zip
8
+ # ZIP End of Central Directory Record
9
+ class EndOfCentralDirectory
10
+ include Constants
11
+
12
+ attr_accessor :signature, :disk_number, :disk_number_with_cd,
13
+ :total_entries_this_disk, :total_entries,
14
+ :central_directory_size, :central_directory_offset,
15
+ :comment_length, :comment
16
+
17
+ def initialize(
18
+ signature: END_OF_CENTRAL_DIRECTORY_SIGNATURE,
19
+ disk_number: 0,
20
+ disk_number_with_cd: 0,
21
+ total_entries_this_disk: 0,
22
+ total_entries: 0,
23
+ central_directory_size: 0,
24
+ central_directory_offset: 0,
25
+ comment_length: 0,
26
+ comment: ""
27
+ )
28
+ @signature = signature
29
+ @disk_number = disk_number
30
+ @disk_number_with_cd = disk_number_with_cd
31
+ @total_entries_this_disk = total_entries_this_disk
32
+ @total_entries = total_entries
33
+ @central_directory_size = central_directory_size
34
+ @central_directory_offset = central_directory_offset
35
+ @comment_length = comment_length
36
+ @comment = comment
37
+ end
38
+
39
+ # Check if ZIP64 format is needed
40
+ def zip64?
41
+ total_entries == 0xFFFF ||
42
+ central_directory_size == ZIP64_LIMIT ||
43
+ central_directory_offset == ZIP64_LIMIT ||
44
+ disk_number == 0xFFFF ||
45
+ disk_number_with_cd == 0xFFFF
46
+ end
47
+
48
+ # Serialize to binary format
49
+ def to_binary
50
+ @comment_length = comment.bytesize
51
+
52
+ [
53
+ signature,
54
+ disk_number,
55
+ disk_number_with_cd,
56
+ total_entries_this_disk,
57
+ total_entries,
58
+ central_directory_size,
59
+ central_directory_offset,
60
+ comment_length,
61
+ ].pack("VvvvvVVv") +
62
+ comment.b
63
+ end
64
+
65
+ # Parse from binary data
66
+ def self.from_binary(data)
67
+ signature, disk_number, disk_number_with_cd,
68
+ total_entries_this_disk, total_entries,
69
+ central_directory_size, central_directory_offset,
70
+ comment_length = data.unpack("VvvvvVVv")
71
+
72
+ unless signature == END_OF_CENTRAL_DIRECTORY_SIGNATURE
73
+ raise Omnizip::FormatError,
74
+ "Invalid EOCD signature"
75
+ end
76
+
77
+ comment = data[22, comment_length].to_s.force_encoding("UTF-8")
78
+
79
+ new(
80
+ signature: signature,
81
+ disk_number: disk_number,
82
+ disk_number_with_cd: disk_number_with_cd,
83
+ total_entries_this_disk: total_entries_this_disk,
84
+ total_entries: total_entries,
85
+ central_directory_size: central_directory_size,
86
+ central_directory_offset: central_directory_offset,
87
+ comment_length: comment_length,
88
+ comment: comment,
89
+ )
90
+ end
91
+
92
+ # Size of the record in bytes
93
+ def record_size
94
+ 22 + comment_length
95
+ end
96
+
97
+ # Find EOCD record by scanning backwards from end of file
98
+ def self.find_in_file(io)
99
+ # Start from the end and work backwards
100
+ # EOCD is at least 22 bytes, can be up to 22 + MAX_COMMENT_LENGTH
101
+ io.seek(0, ::IO::SEEK_END)
102
+ file_size = io.pos
103
+
104
+ # Start searching from the end
105
+ max_comment_size = [file_size - 22, MAX_COMMENT_LENGTH].min
106
+ search_start = [file_size - 22 - max_comment_size, 0].max
107
+
108
+ io.seek(search_start, ::IO::SEEK_SET)
109
+ buffer = io.read(file_size - search_start)
110
+
111
+ # Search for EOCD signature from the end
112
+ signature_bytes = [END_OF_CENTRAL_DIRECTORY_SIGNATURE].pack("V")
113
+
114
+ (buffer.size - 22).downto(0) do |i|
115
+ if buffer[i, 4] == signature_bytes
116
+ # Found potential EOCD
117
+ eocd_data = buffer[i..]
118
+ comment_length = eocd_data[20, 2].unpack1("v")
119
+
120
+ # Verify this is the actual EOCD by checking if comment length matches
121
+ if i + 22 + comment_length == buffer.size
122
+ return from_binary(eocd_data)
123
+ end
124
+ end
125
+ end
126
+
127
+ raise Omnizip::FormatError,
128
+ "Could not find End of Central Directory record"
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+
5
+ module Omnizip
6
+ module Formats
7
+ module Zip
8
+ # ZIP Local File Header
9
+ class LocalFileHeader
10
+ include Constants
11
+
12
+ attr_accessor :signature, :version_needed, :flags, :compression_method,
13
+ :last_mod_time, :last_mod_date, :crc32,
14
+ :compressed_size, :uncompressed_size,
15
+ :filename_length, :extra_field_length,
16
+ :filename, :extra_field
17
+
18
+ def initialize(
19
+ signature: LOCAL_FILE_HEADER_SIGNATURE,
20
+ version_needed: VERSION_DEFAULT,
21
+ flags: 0,
22
+ compression_method: COMPRESSION_STORE,
23
+ last_mod_time: 0,
24
+ last_mod_date: 0,
25
+ crc32: 0,
26
+ compressed_size: 0,
27
+ uncompressed_size: 0,
28
+ filename_length: 0,
29
+ extra_field_length: 0,
30
+ filename: "",
31
+ extra_field: ""
32
+ )
33
+ @signature = signature
34
+ @version_needed = version_needed
35
+ @flags = flags
36
+ @compression_method = compression_method
37
+ @last_mod_time = last_mod_time
38
+ @last_mod_date = last_mod_date
39
+ @crc32 = crc32
40
+ @compressed_size = compressed_size
41
+ @uncompressed_size = uncompressed_size
42
+ @filename_length = filename_length
43
+ @extra_field_length = extra_field_length
44
+ @filename = filename
45
+ @extra_field = extra_field
46
+ end
47
+
48
+ # Check if this is a directory entry
49
+ def directory?
50
+ filename.end_with?("/")
51
+ end
52
+
53
+ # Check if ZIP64 format is needed
54
+ def zip64?
55
+ compressed_size == ZIP64_LIMIT ||
56
+ uncompressed_size == ZIP64_LIMIT
57
+ end
58
+
59
+ # Check if entry is encrypted
60
+ def encrypted?
61
+ flags.anybits?(FLAG_ENCRYPTED)
62
+ end
63
+
64
+ # Check if data descriptor follows
65
+ def has_data_descriptor?
66
+ flags.anybits?(FLAG_DATA_DESCRIPTOR)
67
+ end
68
+
69
+ # Check if UTF-8 encoding is used
70
+ def utf8?
71
+ flags.anybits?(FLAG_UTF8)
72
+ end
73
+
74
+ # Serialize to binary format
75
+ def to_binary
76
+ @filename_length = filename.bytesize
77
+ @extra_field_length = extra_field.bytesize
78
+
79
+ [
80
+ signature,
81
+ version_needed,
82
+ flags,
83
+ compression_method,
84
+ last_mod_time,
85
+ last_mod_date,
86
+ crc32,
87
+ compressed_size,
88
+ uncompressed_size,
89
+ filename_length,
90
+ extra_field_length,
91
+ ].pack("VvvvvvVVVvv") +
92
+ filename.b +
93
+ extra_field.b
94
+ end
95
+
96
+ # Parse from binary data
97
+ def self.from_binary(data)
98
+ signature, version_needed, flags, compression_method,
99
+ last_mod_time, last_mod_date, crc32,
100
+ compressed_size, uncompressed_size,
101
+ filename_length, extra_field_length = data.unpack("VvvvvvVVVvv")
102
+
103
+ unless signature == LOCAL_FILE_HEADER_SIGNATURE
104
+ raise Omnizip::FormatError,
105
+ "Invalid local file header signature"
106
+ end
107
+
108
+ offset = 30
109
+ filename = data[offset, filename_length].force_encoding("UTF-8")
110
+ offset += filename_length
111
+
112
+ extra_field = data[offset, extra_field_length]
113
+
114
+ new(
115
+ signature: signature,
116
+ version_needed: version_needed,
117
+ flags: flags,
118
+ compression_method: compression_method,
119
+ last_mod_time: last_mod_time,
120
+ last_mod_date: last_mod_date,
121
+ crc32: crc32,
122
+ compressed_size: compressed_size,
123
+ uncompressed_size: uncompressed_size,
124
+ filename_length: filename_length,
125
+ extra_field_length: extra_field_length,
126
+ filename: filename,
127
+ extra_field: extra_field,
128
+ )
129
+ end
130
+
131
+ # Size of the header in bytes
132
+ def header_size
133
+ 30 + filename_length + extra_field_length
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "local_file_header"
5
+ require_relative "central_directory_header"
6
+ require_relative "end_of_central_directory"
7
+ require_relative "unix_extra_field"
8
+ require_relative "../../link_handler"
9
+
10
+ module Omnizip
11
+ module Formats
12
+ module Zip
13
+ # ZIP archive reader
14
+ class Reader
15
+ include Constants
16
+
17
+ attr_reader :file_path, :entries
18
+
19
+ def initialize(file_path)
20
+ @file_path = file_path
21
+ @entries = []
22
+ @central_directory = []
23
+ end
24
+
25
+ # Read and parse the ZIP archive
26
+ def read
27
+ File.open(file_path, "rb") do |io|
28
+ read_from_io(io)
29
+ end
30
+ self
31
+ end
32
+
33
+ # Read from an IO object
34
+ def read_from_io(io)
35
+ # Find and read End of Central Directory
36
+ eocd = EndOfCentralDirectory.find_in_file(io)
37
+
38
+ # Read Central Directory
39
+ read_central_directory(io, eocd)
40
+
41
+ self
42
+ end
43
+
44
+ # Extract all files to a directory
45
+ def extract_all(output_dir, preserve_links: true,
46
+ dereference_links: false)
47
+ entries.each do |entry|
48
+ extract_entry(entry, output_dir, preserve_links: preserve_links,
49
+ dereference_links: dereference_links)
50
+ end
51
+ end
52
+
53
+ # Extract a specific entry
54
+ def extract_entry(entry, output_dir, preserve_links: true,
55
+ dereference_links: false)
56
+ output_path = File.join(output_dir, entry.filename)
57
+
58
+ if entry.directory?
59
+ FileUtils.mkdir_p(output_path)
60
+ elsif preserve_links && !dereference_links && entry.symlink?
61
+ extract_symlink(entry, output_dir)
62
+ else
63
+ FileUtils.mkdir_p(File.dirname(output_path))
64
+
65
+ File.open(file_path, "rb") do |io|
66
+ # Seek to local file header
67
+ io.seek(entry.local_header_offset, ::IO::SEEK_SET)
68
+
69
+ # Read fixed part of local file header (30 bytes)
70
+ fixed_header = io.read(30)
71
+
72
+ # Extract variable lengths from fixed header
73
+ _signature, _version, _flags, _method, _time, _date, _crc32,
74
+ _comp_size, _uncomp_size, filename_length, extra_length = fixed_header.unpack("VvvvvvVVVvv")
75
+
76
+ # Read variable parts
77
+ variable_data = io.read(filename_length + extra_length)
78
+
79
+ # Parse complete local file header
80
+ LocalFileHeader.from_binary(fixed_header + variable_data)
81
+
82
+ # Now we're positioned right after the local file header, read compressed data
83
+ compressed_data = io.read(entry.compressed_size)
84
+
85
+ # Decompress data
86
+ decompressed_data = decompress_data(
87
+ compressed_data,
88
+ entry.compression_method,
89
+ entry.uncompressed_size,
90
+ )
91
+
92
+ # Verify CRC
93
+ calculated_crc = Omnizip::Checksums::Crc32.new.tap do |c|
94
+ c.update(decompressed_data)
95
+ end.finalize
96
+ if calculated_crc != entry.crc32
97
+ raise Omnizip::ChecksumError,
98
+ "CRC mismatch for #{entry.filename}"
99
+ end
100
+
101
+ # Write decompressed data
102
+ File.binwrite(output_path, decompressed_data)
103
+
104
+ # Set file permissions if Unix
105
+ if entry.unix_permissions.positive?
106
+ File.chmod(entry.unix_permissions & 0o777, output_path)
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ # Extract a symbolic link
113
+ def extract_symlink(entry, output_dir)
114
+ output_path = File.join(output_dir, entry.filename)
115
+
116
+ unless LinkHandler.symlink_supported?
117
+ warn "Warning: Symbolic links not supported on #{RUBY_PLATFORM}, extracting as regular file"
118
+ extract_entry(entry, output_dir, preserve_links: false)
119
+ return
120
+ end
121
+
122
+ target = entry.link_target
123
+ unless target
124
+ warn "Warning: No link target found for #{entry.filename}, skipping"
125
+ return
126
+ end
127
+
128
+ FileUtils.mkdir_p(File.dirname(output_path))
129
+
130
+ # Remove existing file/link if present
131
+ FileUtils.rm_f(output_path) if File.exist?(output_path) || File.symlink?(output_path)
132
+
133
+ LinkHandler.create_symlink(target, output_path)
134
+ end
135
+
136
+ # List all entries in the archive
137
+ def list_entries(show_links: false)
138
+ entries.map do |entry|
139
+ info = {
140
+ filename: entry.filename,
141
+ compressed_size: entry.compressed_size,
142
+ uncompressed_size: entry.uncompressed_size,
143
+ compression_method: compression_method_name(entry.compression_method),
144
+ crc32: entry.crc32,
145
+ directory: entry.directory?,
146
+ }
147
+
148
+ if show_links && entry.symlink?
149
+ info[:symlink] = true
150
+ info[:link_target] = entry.link_target
151
+ end
152
+
153
+ info
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ # Read central directory entries
160
+ def read_central_directory(io, eocd)
161
+ io.seek(eocd.central_directory_offset, ::IO::SEEK_SET)
162
+
163
+ eocd.total_entries.times do
164
+ header_data = io.read(46)
165
+ break unless header_data && header_data.size == 46
166
+
167
+ # Get dynamic field lengths
168
+ _, _, _, _, _, _, _, _, _, _,
169
+ filename_length, extra_field_length, comment_length = header_data.unpack("VvvvvvvVVVvvv")
170
+
171
+ # Read complete header
172
+ complete_data = header_data + io.read(filename_length + extra_field_length + comment_length)
173
+ entry = CentralDirectoryHeader.from_binary(complete_data)
174
+
175
+ @entries << entry
176
+ @central_directory << entry
177
+ end
178
+ end
179
+
180
+ # Decompress data based on compression method
181
+ def decompress_data(compressed_data, method, uncompressed_size)
182
+ case method
183
+ when COMPRESSION_STORE
184
+ compressed_data
185
+ when COMPRESSION_DEFLATE
186
+ decompress_deflate(compressed_data)
187
+ when COMPRESSION_BZIP2
188
+ decompress_bzip2(compressed_data)
189
+ when COMPRESSION_LZMA
190
+ decompress_lzma(compressed_data, uncompressed_size)
191
+ when COMPRESSION_ZSTANDARD
192
+ decompress_zstandard(compressed_data)
193
+ else
194
+ raise Omnizip::UnsupportedFormatError,
195
+ "Unsupported compression method: #{method}"
196
+ end
197
+ end
198
+
199
+ # Decompress using Deflate
200
+ def decompress_deflate(data)
201
+ require "zlib"
202
+ # ZIP uses raw deflate without zlib wrapper
203
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(data)
204
+ rescue StandardError => e
205
+ raise Omnizip::DecompressionError,
206
+ "Deflate decompression failed: #{e.message}"
207
+ end
208
+
209
+ # Decompress using BZip2
210
+ def decompress_bzip2(data)
211
+ algorithm = AlgorithmRegistry.get(:bzip2)
212
+ algorithm.decompress(data)
213
+ rescue StandardError => e
214
+ raise Omnizip::DecompressionError,
215
+ "BZip2 decompression failed: #{e.message}"
216
+ end
217
+
218
+ # Decompress using LZMA
219
+ def decompress_lzma(data, uncompressed_size)
220
+ algorithm = AlgorithmRegistry.get(:lzma)
221
+ algorithm.decompress(data, uncompressed_size: uncompressed_size)
222
+ rescue StandardError => e
223
+ raise Omnizip::DecompressionError,
224
+ "LZMA decompression failed: #{e.message}"
225
+ end
226
+
227
+ # Decompress using Zstandard
228
+ def decompress_zstandard(data)
229
+ algorithm = AlgorithmRegistry.get(:zstandard)
230
+ algorithm.decompress(data)
231
+ rescue StandardError => e
232
+ raise Omnizip::DecompressionError,
233
+ "Zstandard decompression failed: #{e.message}"
234
+ end
235
+
236
+ # Get human-readable compression method name
237
+ def compression_method_name(method)
238
+ case method
239
+ when COMPRESSION_STORE then "Store"
240
+ when COMPRESSION_DEFLATE then "Deflate"
241
+ when COMPRESSION_BZIP2 then "BZip2"
242
+ when COMPRESSION_LZMA then "LZMA"
243
+ when COMPRESSION_ZSTANDARD then "Zstandard"
244
+ else "Unknown (#{method})"
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Formats
5
+ module Zip
6
+ # Info-ZIP Unix extra field (tag 0x7875)
7
+ # Stores Unix-specific metadata including symbolic link targets
8
+ class UnixExtraField
9
+ UNIX_EXTRA_FIELD_TAG = 0x7875
10
+
11
+ attr_accessor :version, :uid_size, :uid, :gid_size, :gid, :link_target
12
+
13
+ def initialize(
14
+ version: 1,
15
+ uid: nil,
16
+ gid: nil,
17
+ link_target: nil
18
+ )
19
+ @version = version
20
+ @uid = uid
21
+ @gid = gid
22
+ @link_target = link_target
23
+
24
+ # Calculate sizes
25
+ @uid_size = uid ? [uid].pack("V").bytesize : 0
26
+ @gid_size = gid ? [gid].pack("V").bytesize : 0
27
+ end
28
+
29
+ # Check if this field contains a symbolic link target
30
+ def symlink?
31
+ !@link_target.nil? && !@link_target.empty?
32
+ end
33
+
34
+ # Serialize to binary format
35
+ def to_binary
36
+ data = [version].pack("C")
37
+
38
+ # Add UID if present
39
+ if @uid
40
+ data << [@uid_size].pack("C")
41
+ data << [@uid].pack("V")[0, @uid_size]
42
+ else
43
+ data << [0].pack("C")
44
+ end
45
+
46
+ # Add GID if present
47
+ if @gid
48
+ data << [@gid_size].pack("C")
49
+ data << [@gid].pack("V")[0, @gid_size]
50
+ else
51
+ data << [0].pack("C")
52
+ end
53
+
54
+ # Add link target if present (for symbolic links)
55
+ data << @link_target.b if @link_target
56
+
57
+ # Return with tag and size
58
+ [
59
+ UNIX_EXTRA_FIELD_TAG,
60
+ data.bytesize,
61
+ ].pack("vv") + data
62
+ end
63
+
64
+ # Parse from binary data
65
+ def self.from_binary(data)
66
+ return nil if data.nil? || data.bytesize < 3
67
+
68
+ version = data.unpack1("C")
69
+ offset = 1
70
+
71
+ # Read UID
72
+ uid_size = data[offset].unpack1("C")
73
+ offset += 1
74
+ uid = (data[offset, uid_size].unpack1("V") if uid_size.positive?)
75
+ offset += uid_size
76
+
77
+ # Read GID
78
+ gid_size = data[offset].unpack1("C")
79
+ offset += 1
80
+ gid = (data[offset, gid_size].unpack1("V") if gid_size.positive?)
81
+ offset += gid_size
82
+
83
+ # Read link target if present
84
+ link_target = if offset < data.bytesize
85
+ data[offset..].force_encoding("UTF-8")
86
+ end
87
+
88
+ new(
89
+ version: version,
90
+ uid: uid,
91
+ gid: gid,
92
+ link_target: link_target,
93
+ )
94
+ end
95
+
96
+ # Parse extra field from complete extra field data
97
+ def self.find_in_extra_field(extra_field_data)
98
+ return nil if extra_field_data.nil? || extra_field_data.empty?
99
+
100
+ offset = 0
101
+ while offset < extra_field_data.bytesize - 4
102
+ tag, size = extra_field_data[offset, 4].unpack("vv")
103
+
104
+ if tag == UNIX_EXTRA_FIELD_TAG
105
+ field_data = extra_field_data[offset + 4, size]
106
+ return from_binary(field_data)
107
+ end
108
+
109
+ offset += 4 + size
110
+ end
111
+
112
+ nil
113
+ end
114
+
115
+ # Create a Unix extra field for a symbolic link
116
+ def self.for_symlink(target, uid: nil, gid: nil)
117
+ new(
118
+ version: 1,
119
+ uid: uid,
120
+ gid: gid,
121
+ link_target: target,
122
+ )
123
+ end
124
+
125
+ # Create a Unix extra field for a hard link
126
+ def self.for_hardlink(uid: nil, gid: nil)
127
+ new(
128
+ version: 1,
129
+ uid: uid,
130
+ gid: gid,
131
+ link_target: nil,
132
+ )
133
+ end
134
+
135
+ # Get the size of this extra field in bytes
136
+ def size
137
+ to_binary.bytesize
138
+ end
139
+
140
+ # Convert to hash representation
141
+ def to_h
142
+ {
143
+ tag: UNIX_EXTRA_FIELD_TAG,
144
+ version: @version,
145
+ uid: @uid,
146
+ gid: @gid,
147
+ link_target: @link_target,
148
+ }
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end