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,427 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (C) 2024 Ribose Inc.
5
+ #
6
+ # This file is part of Omnizip.
7
+ #
8
+ # Omnizip is a pure Ruby port of 7-Zip compression algorithms.
9
+ # Based on the 7-Zip LZMA SDK by Igor Pavlov.
10
+ #
11
+ # This library is free software; you can redistribute it and/or
12
+ # modify it under the terms of the GNU Lesser General Public
13
+ # License as published by the Free Software Foundation; either
14
+ # version 2.1 of the License, or (at your option) any later version.
15
+ #
16
+ # See the COPYING file for the complete text of the license.
17
+ #
18
+
19
+ require_relative "../cli/output_formatter"
20
+ require_relative "../formats/seven_zip/writer"
21
+
22
+ module Omnizip
23
+ module Commands
24
+ # Command to create .7z archives from files and directories.
25
+ class ArchiveCreateCommand
26
+ attr_reader :options
27
+
28
+ # Initialize archive create command with options.
29
+ #
30
+ # @param options [Hash] Command options from Thor
31
+ def initialize(options = {})
32
+ @options = options
33
+ end
34
+
35
+ # Execute the archive create command.
36
+ #
37
+ # @param output_file [String] Path to output archive
38
+ # @param inputs [Array<String>] Paths to files/directories to archive
39
+ # @return [void]
40
+ def run(output_file, *inputs)
41
+ validate_inputs(output_file, inputs)
42
+
43
+ # Apply profile settings if specified
44
+ opts = options.dup
45
+ if opts[:profile]
46
+ first_file = find_first_file(inputs)
47
+ opts = apply_profile(first_file, opts)
48
+ end
49
+
50
+ # Determine format from extension or --format option
51
+ format = opts[:format] || detect_format(output_file)
52
+
53
+ if format == "rar"
54
+ create_rar_archive(output_file, inputs, opts)
55
+ else
56
+ create_7z_archive(output_file, inputs, opts)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def detect_format(filename)
63
+ case File.extname(filename).downcase
64
+ when ".rar" then "rar"
65
+ else "7z"
66
+ end
67
+ end
68
+
69
+ def create_rar_archive(output_file, inputs, opts)
70
+ version = opts[:rar_version] || 5
71
+ compression = (opts[:rar_compression] || "store").to_sym
72
+ level = opts[:level] || 3
73
+ include_mtime = opts[:include_mtime] || false
74
+ include_crc32 = opts[:include_crc32] || false
75
+ solid = opts[:solid] || false
76
+ multi_volume = opts[:multi_volume] || false
77
+ volume_size = opts[:volume_size]
78
+ volume_naming = opts[:volume_naming] || "part"
79
+ password = opts[:password]
80
+ kdf_iterations = opts[:kdf_iterations] || 262_144
81
+ recovery = opts[:recovery] || false
82
+ recovery_percent = opts[:recovery_percent] || 5
83
+ verbose = opts[:verbose] || false
84
+
85
+ if verbose
86
+ CliOutputFormatter.verbose_puts(
87
+ "Creating RAR#{version} archive: #{output_file}",
88
+ true,
89
+ )
90
+ CliOutputFormatter.verbose_puts(
91
+ "Compression: #{compression}, Level: #{level}",
92
+ true,
93
+ )
94
+ CliOutputFormatter.verbose_puts(
95
+ "Include mtime: #{include_mtime}, Include CRC32: #{include_crc32}",
96
+ true,
97
+ )
98
+ if solid
99
+ CliOutputFormatter.verbose_puts(
100
+ "Solid compression: enabled",
101
+ true,
102
+ )
103
+ end
104
+ if multi_volume && volume_size
105
+ CliOutputFormatter.verbose_puts(
106
+ "Multi-volume: enabled (size: #{volume_size}, naming: #{volume_naming})",
107
+ true,
108
+ )
109
+ end
110
+ if password
111
+ CliOutputFormatter.verbose_puts(
112
+ "Encryption: enabled (AES-256-CBC, KDF iterations: #{kdf_iterations})",
113
+ true,
114
+ )
115
+ end
116
+ if recovery
117
+ CliOutputFormatter.verbose_puts(
118
+ "PAR2 recovery: enabled (redundancy: #{recovery_percent}%)",
119
+ true,
120
+ )
121
+ end
122
+ end
123
+
124
+ start_time = Time.now
125
+
126
+ require_relative "../formats/rar"
127
+
128
+ writer_opts = {
129
+ version: version,
130
+ compression: compression,
131
+ level: level,
132
+ include_mtime: include_mtime,
133
+ include_crc32: include_crc32,
134
+ }
135
+
136
+ # Add solid compression for RAR5
137
+ writer_opts[:solid] = solid if version == 5 && solid
138
+
139
+ # Add multi-volume options for RAR5
140
+ if version == 5 && multi_volume && volume_size
141
+ writer_opts[:multi_volume] = true
142
+ writer_opts[:volume_size] = volume_size
143
+ writer_opts[:volume_naming] = volume_naming
144
+ end
145
+
146
+ # Add encryption options for RAR5
147
+ if version == 5 && password
148
+ writer_opts[:password] = password
149
+ writer_opts[:kdf_iterations] = kdf_iterations
150
+ end
151
+
152
+ # Add recovery options for RAR5
153
+ if version == 5 && recovery
154
+ writer_opts[:recovery] = true
155
+ writer_opts[:recovery_percent] = recovery_percent
156
+ end
157
+
158
+ result_files = Omnizip::Formats::Rar.create(output_file,
159
+ writer_opts) do |rar|
160
+ inputs.each do |input|
161
+ if File.directory?(input)
162
+ raise ArgumentError,
163
+ "RAR5 writer does not support directories yet. Add individual files."
164
+ else
165
+ CliOutputFormatter.verbose_puts(
166
+ "Adding file: #{input}",
167
+ verbose,
168
+ )
169
+ rar.add_file(input)
170
+ end
171
+ end
172
+ end
173
+
174
+ elapsed = Time.now - start_time
175
+
176
+ # Handle result based on whether recovery or multi-volume is enabled
177
+ files = result_files.is_a?(Array) ? result_files : [result_files]
178
+ files.find { |f| f.end_with?(".rar") } || files.first
179
+ archive_size = files.sum { |f| File.size(f) }
180
+
181
+ if verbose
182
+ puts ""
183
+ puts "Archive created successfully"
184
+ if files.size > 1
185
+ puts "Files created: #{files.size}"
186
+ puts " Archive volumes: #{files.count { |f| f.end_with?('.rar') }}"
187
+ if recovery
188
+ puts " PAR2 files: #{files.count { |f| f.include?('.par2') }}"
189
+ end
190
+ puts "Total size: #{format_bytes(archive_size)}"
191
+ else
192
+ puts "Archive size: #{format_bytes(archive_size)}"
193
+ end
194
+ puts "Time elapsed: #{elapsed.round(2)}s"
195
+ elsif files.size == 1
196
+ puts "Created: #{files.first}"
197
+ else
198
+ puts "Created: #{files.size} files (#{files.count do |f|
199
+ f.end_with?('.rar')
200
+ end} volumes)"
201
+ end
202
+ end
203
+
204
+ def create_7z_archive(output_file, inputs, opts)
205
+ algorithm = (opts[:algorithm] || "lzma2").to_sym
206
+ level = opts[:level] || 5
207
+ solid = opts.fetch(:solid, true)
208
+ verbose = opts[:verbose] || false
209
+ filters = parse_filters(opts[:filters])
210
+ volume_size = parse_volume_size(opts[:volume_size])
211
+ password = opts[:password]
212
+ encrypt_headers = opts[:encrypt_headers] || false
213
+ preserve_ntfs_streams = opts[:preserve_ntfs_streams] || false
214
+
215
+ if verbose
216
+ CliOutputFormatter.verbose_puts(
217
+ "Creating archive: #{output_file}",
218
+ true,
219
+ )
220
+ CliOutputFormatter.verbose_puts(
221
+ "Algorithm: #{algorithm}, Level: #{level}, " \
222
+ "Solid: #{solid}",
223
+ true,
224
+ )
225
+ if volume_size
226
+ CliOutputFormatter.verbose_puts(
227
+ "Volume size: #{format_bytes(volume_size)}",
228
+ true,
229
+ )
230
+ end
231
+ unless filters.empty?
232
+ CliOutputFormatter.verbose_puts(
233
+ "Filters: #{filters.join(', ')}",
234
+ true,
235
+ )
236
+ end
237
+ if encrypt_headers
238
+ CliOutputFormatter.verbose_puts(
239
+ "Header encryption: enabled",
240
+ true,
241
+ )
242
+ end
243
+ if preserve_ntfs_streams && Omnizip::Platform.supports_ntfs_streams?
244
+ CliOutputFormatter.verbose_puts(
245
+ "NTFS streams: preserving",
246
+ true,
247
+ )
248
+ end
249
+ end
250
+
251
+ start_time = Time.now
252
+
253
+ create_7z_archive_writer(output_file, inputs, algorithm, level, solid,
254
+ filters, volume_size, password, encrypt_headers,
255
+ preserve_ntfs_streams, verbose)
256
+
257
+ elapsed = Time.now - start_time
258
+
259
+ archive_size = calculate_archive_size(output_file, volume_size)
260
+
261
+ if verbose
262
+ puts ""
263
+ puts "Archive created successfully"
264
+ if volume_size
265
+ puts "Total size: #{format_bytes(archive_size)}"
266
+ puts "Volumes: #{count_volumes(output_file)}"
267
+ else
268
+ puts "Archive size: #{format_bytes(archive_size)}"
269
+ end
270
+ puts "Time elapsed: #{elapsed.round(2)}s"
271
+ else
272
+ puts "Created: #{output_file}"
273
+ end
274
+ end
275
+
276
+ def validate_inputs(output_file, inputs)
277
+ raise Omnizip::IOError, "No input files specified" if
278
+ inputs.empty?
279
+
280
+ inputs.each do |input|
281
+ unless File.exist?(input)
282
+ raise Omnizip::IOError, "Input not found: #{input}"
283
+ end
284
+ end
285
+
286
+ output_dir = File.dirname(output_file)
287
+ unless File.directory?(output_dir)
288
+ raise Omnizip::IOError,
289
+ "Output directory does not exist: #{output_dir}"
290
+ end
291
+
292
+ return if File.writable?(output_dir)
293
+
294
+ raise Omnizip::IOError,
295
+ "Output directory not writable: #{output_dir}"
296
+ end
297
+
298
+ def parse_filters(filter_str)
299
+ return [] if filter_str.nil? || filter_str.empty?
300
+
301
+ filter_str.split(",").map(&:strip).map(&:to_sym)
302
+ end
303
+
304
+ def create_7z_archive_writer(output_file, inputs, algorithm, level, solid,
305
+ filters, volume_size, password, encrypt_headers,
306
+ _preserve_ntfs_streams, verbose)
307
+ writer_opts = {
308
+ algorithm: algorithm,
309
+ level: level,
310
+ solid: solid,
311
+ filters: filters,
312
+ }
313
+ writer_opts[:volume_size] = volume_size if volume_size
314
+ writer_opts[:password] = password if password
315
+ writer_opts[:encrypt_headers] = encrypt_headers if encrypt_headers
316
+
317
+ writer = Formats::SevenZip::Writer.new(output_file, writer_opts)
318
+
319
+ inputs.each do |input|
320
+ if File.directory?(input)
321
+ CliOutputFormatter.verbose_puts(
322
+ "Adding directory: #{input}",
323
+ verbose,
324
+ )
325
+ writer.add_directory(input)
326
+ else
327
+ CliOutputFormatter.verbose_puts(
328
+ "Adding file: #{input}",
329
+ verbose,
330
+ )
331
+ writer.add_file(input)
332
+ end
333
+ end
334
+
335
+ CliOutputFormatter.verbose_puts("Writing archive...", verbose)
336
+ writer.write
337
+ rescue StandardError => e
338
+ raise Omnizip::CompressionError,
339
+ "Failed to create archive: #{e.message}"
340
+ end
341
+
342
+ def format_bytes(bytes)
343
+ units = %w[B KB MB GB]
344
+ size = bytes.to_f
345
+ unit_idx = 0
346
+
347
+ while size >= 1024 && unit_idx < units.length - 1
348
+ size /= 1024.0
349
+ unit_idx += 1
350
+ end
351
+
352
+ format("%.2f %s", size, units[unit_idx])
353
+ end
354
+
355
+ def apply_profile(file_path, options)
356
+ profile_spec = options.delete(:profile)
357
+ return options unless profile_spec
358
+
359
+ # Get the profile
360
+ profile = case profile_spec
361
+ when "auto"
362
+ file_path ? Omnizip::Profile.detect(file_path) : Omnizip::Profile.get(:balanced)
363
+ else
364
+ Omnizip::Profile.get(profile_spec.to_sym) || Omnizip::Profile.get(:balanced)
365
+ end
366
+
367
+ # Apply profile to options
368
+ profile.apply_to(options)
369
+ end
370
+
371
+ def find_first_file(inputs)
372
+ inputs.each do |input|
373
+ return input if File.file?(input)
374
+
375
+ # Check directories for first file
376
+ if File.directory?(input)
377
+ Dir.foreach(input) do |entry|
378
+ next if [".", ".."].include?(entry)
379
+
380
+ full_path = File.join(input, entry)
381
+ return full_path if File.file?(full_path)
382
+ end
383
+ end
384
+ end
385
+ nil
386
+ end
387
+
388
+ def parse_volume_size(size_str)
389
+ return nil if size_str.nil? || size_str.empty?
390
+
391
+ require_relative "../models/split_options"
392
+ Omnizip::Models::SplitOptions.parse_volume_size(size_str)
393
+ end
394
+
395
+ def calculate_archive_size(output_file, volume_size)
396
+ if volume_size
397
+ # Count all volumes
398
+ base = output_file.sub(/\.\d{3}$/, "")
399
+ total = 0
400
+ volume_num = 1
401
+ loop do
402
+ volume_path = format("%s.%03d", base, volume_num)
403
+ break unless File.exist?(volume_path)
404
+
405
+ total += File.size(volume_path)
406
+ volume_num += 1
407
+ end
408
+ total
409
+ else
410
+ File.size(output_file)
411
+ end
412
+ end
413
+
414
+ def count_volumes(output_file)
415
+ base = output_file.sub(/\.\d{3}$/, "")
416
+ count = 0
417
+ loop do
418
+ volume_path = format("%s.%03d", base, count + 1)
419
+ break unless File.exist?(volume_path)
420
+
421
+ count += 1
422
+ end
423
+ count
424
+ end
425
+ end
426
+ end
427
+ end
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (C) 2024 Ribose Inc.
5
+ #
6
+ # This file is part of Omnizip.
7
+ #
8
+ # Omnizip is a pure Ruby port of 7-Zip compression algorithms.
9
+ # Based on the 7-Zip LZMA SDK by Igor Pavlov.
10
+ #
11
+ # This library is free software; you can redistribute it and/or
12
+ # modify it under the terms of the GNU Lesser General Public
13
+ # License as published by the Free Software Foundation; either
14
+ # version 2.1 of the License, or (at your option) any later version.
15
+ #
16
+ # See the COPYING file for the complete text of the license.
17
+ #
18
+
19
+ require_relative "../cli/output_formatter"
20
+ require_relative "../formats/seven_zip/reader"
21
+ require_relative "../extraction"
22
+
23
+ module Omnizip
24
+ module Commands
25
+ # Command to extract .7z archives to a directory.
26
+ class ArchiveExtractCommand
27
+ attr_reader :options
28
+
29
+ # Initialize archive extract command with options.
30
+ #
31
+ # @param options [Hash] Command options from Thor
32
+ def initialize(options = {})
33
+ @options = options
34
+ end
35
+
36
+ # Execute the archive extract command.
37
+ #
38
+ # @param archive_file [String] Path to .7z archive
39
+ # @param output_dir [String] Directory to extract to
40
+ # @return [void]
41
+ def run(archive_file, output_dir = nil)
42
+ validate_input(archive_file)
43
+
44
+ # Default to current directory if not specified
45
+ output_dir ||= "."
46
+ output_dir = File.expand_path(output_dir)
47
+
48
+ verbose = options[:verbose] || false
49
+ patterns = Array(options[:pattern]) if options[:pattern]
50
+ excludes = Array(options[:exclude]) if options[:exclude]
51
+ regex = options[:regex]
52
+
53
+ if verbose
54
+ CliOutputFormatter.verbose_puts(
55
+ "Extracting archive: #{archive_file}",
56
+ true,
57
+ )
58
+ CliOutputFormatter.verbose_puts(
59
+ "Output directory: #{output_dir}",
60
+ true,
61
+ )
62
+ if patterns
63
+ CliOutputFormatter.verbose_puts(
64
+ "Include patterns: #{patterns.join(', ')}",
65
+ true,
66
+ )
67
+ end
68
+ if excludes
69
+ CliOutputFormatter.verbose_puts(
70
+ "Exclude patterns: #{excludes.join(', ')}",
71
+ true,
72
+ )
73
+ end
74
+ if regex
75
+ CliOutputFormatter.verbose_puts(
76
+ "Regex pattern: #{regex}",
77
+ true,
78
+ )
79
+ end
80
+ end
81
+
82
+ start_time = Time.now
83
+
84
+ file_count = if patterns || excludes || regex
85
+ extract_with_patterns(archive_file, output_dir, verbose)
86
+ else
87
+ extract_archive(archive_file, output_dir, verbose)
88
+ end
89
+
90
+ elapsed = Time.now - start_time
91
+
92
+ if verbose
93
+ puts ""
94
+ puts "Extraction completed successfully"
95
+ puts "Files extracted: #{file_count}"
96
+ puts "Time elapsed: #{elapsed.round(2)}s"
97
+ else
98
+ puts "Extracted #{file_count} file(s) to: #{output_dir}"
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def validate_input(archive_file)
105
+ unless File.exist?(archive_file)
106
+ raise Omnizip::IOError, "Archive not found: #{archive_file}"
107
+ end
108
+
109
+ return if File.readable?(archive_file)
110
+
111
+ raise Omnizip::IOError,
112
+ "Archive not readable: #{archive_file}"
113
+ end
114
+
115
+ def extract_archive(archive_file, output_dir, verbose)
116
+ reader = case File.extname(archive_file).downcase
117
+ when ".rar"
118
+ Formats::Rar::Reader.new(archive_file).open
119
+ when ".tar"
120
+ Formats::Tar::Reader.new(archive_file).read
121
+ when ".gz", ".gzip"
122
+ # GZIP files are single-file compression, extract directly
123
+ extract_gzip(archive_file, output_dir, verbose)
124
+ return 1
125
+ when ".bz2", ".bzip2"
126
+ # BZIP2 files are single-file compression, extract directly
127
+ extract_bzip2(archive_file, output_dir, verbose)
128
+ return 1
129
+ when ".xz"
130
+ # XZ files are single-file compression, extract directly
131
+ extract_xz(archive_file, output_dir, verbose)
132
+ return 1
133
+ else
134
+ Formats::SevenZip::Reader.new(archive_file).open
135
+ end
136
+ file_count = 0
137
+
138
+ reader.entries.each do |entry|
139
+ output_path = File.join(output_dir, entry.name)
140
+
141
+ if entry.directory?
142
+ CliOutputFormatter.verbose_puts(
143
+ "Creating directory: #{entry.name}",
144
+ verbose,
145
+ )
146
+ FileUtils.mkdir_p(output_path)
147
+ else
148
+ CliOutputFormatter.verbose_puts(
149
+ "Extracting: #{entry.name}",
150
+ verbose,
151
+ )
152
+
153
+ # Ensure parent directory exists
154
+ FileUtils.mkdir_p(File.dirname(output_path))
155
+
156
+ # Extract file
157
+ reader.extract_entry(entry.name, output_path)
158
+
159
+ file_count += 1
160
+ end
161
+ end
162
+
163
+ file_count
164
+ rescue StandardError => e
165
+ raise Omnizip::CompressionError,
166
+ "Failed to extract archive: #{e.message}"
167
+ end
168
+
169
+ def extract_with_patterns(archive_file, output_dir, verbose)
170
+ # Determine archive type and open appropriately
171
+ archive = case File.extname(archive_file).downcase
172
+ when ".zip"
173
+ Omnizip::Zip::File.open(archive_file)
174
+ when ".rar"
175
+ Formats::Rar::Reader.new(archive_file).open
176
+ when ".tar"
177
+ Formats::Tar::Reader.new(archive_file).read
178
+ else
179
+ Formats::SevenZip::Reader.new(archive_file).open
180
+ end
181
+
182
+ # Build filter chain
183
+ filter = Extraction::FilterChain.new
184
+
185
+ # Add include patterns
186
+ if options[:pattern]
187
+ Array(options[:pattern]).each do |pattern|
188
+ filter.include_pattern(pattern)
189
+ end
190
+ end
191
+
192
+ # Add regex pattern
193
+ if options[:regex]
194
+ filter.include_pattern(Regexp.new(options[:regex]))
195
+ end
196
+
197
+ # Add exclude patterns
198
+ if options[:exclude]
199
+ Array(options[:exclude]).each do |pattern|
200
+ filter.exclude_pattern(pattern)
201
+ end
202
+ end
203
+
204
+ # Extract with filter
205
+ extract_options = {
206
+ preserve_paths: !options[:flatten],
207
+ flatten: options[:flatten] || false,
208
+ overwrite: true,
209
+ }
210
+
211
+ extracted = Extraction.extract_with_filter(
212
+ archive,
213
+ filter,
214
+ output_dir,
215
+ extract_options,
216
+ )
217
+
218
+ if verbose
219
+ extracted.each do |path|
220
+ CliOutputFormatter.verbose_puts(
221
+ "Extracted: #{File.basename(path)}",
222
+ verbose,
223
+ )
224
+ end
225
+ end
226
+
227
+ extracted.size
228
+ ensure
229
+ archive&.close if archive.respond_to?(:close)
230
+ end
231
+
232
+ def extract_gzip(archive_file, output_dir, verbose)
233
+ output_file = File.join(
234
+ output_dir,
235
+ File.basename(archive_file, ".*"),
236
+ )
237
+ CliOutputFormatter.verbose_puts(
238
+ "Decompressing GZIP: #{archive_file}",
239
+ verbose,
240
+ )
241
+ FileUtils.mkdir_p(output_dir)
242
+ Formats::Gzip.decompress(archive_file, output_file)
243
+ end
244
+
245
+ def extract_bzip2(archive_file, output_dir, verbose)
246
+ output_file = File.join(
247
+ output_dir,
248
+ File.basename(archive_file, ".*"),
249
+ )
250
+ CliOutputFormatter.verbose_puts(
251
+ "Decompressing BZIP2: #{archive_file}",
252
+ verbose,
253
+ )
254
+ FileUtils.mkdir_p(output_dir)
255
+ Formats::Bzip2File.decompress(archive_file, output_file)
256
+ end
257
+
258
+ def extract_xz(archive_file, output_dir, verbose)
259
+ output_file = File.join(
260
+ output_dir,
261
+ File.basename(archive_file, ".*"),
262
+ )
263
+ CliOutputFormatter.verbose_puts(
264
+ "Decompressing XZ: #{archive_file}",
265
+ verbose,
266
+ )
267
+ FileUtils.mkdir_p(output_dir)
268
+ Formats::Xz.decompress(archive_file, output_file)
269
+ end
270
+ end
271
+ end
272
+ end