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,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "seven_zip/constants"
4
+ require_relative "seven_zip/header"
5
+ require_relative "seven_zip/parser"
6
+ require_relative "seven_zip/reader"
7
+ require_relative "seven_zip/writer"
8
+ require_relative "seven_zip/coder_chain"
9
+ require_relative "seven_zip/stream_decompressor"
10
+ require_relative "seven_zip/stream_compressor"
11
+ require_relative "seven_zip/file_collector"
12
+ require_relative "seven_zip/header_writer"
13
+ require_relative "seven_zip/split_archive_reader"
14
+ require_relative "seven_zip/split_archive_writer"
15
+ require_relative "../models/split_options"
16
+
17
+ module Omnizip
18
+ module Formats
19
+ # .7z archive format support
20
+ # Provides read and write access to 7-Zip archives
21
+ #
22
+ # This module implements the .7z archive format specification,
23
+ # supporting:
24
+ # - Format signature and header validation
25
+ # - Archive structure parsing
26
+ # - File extraction
27
+ # - Archive creation
28
+ # - Split archives (multi-volume)
29
+ module SevenZip
30
+ # Create a new .7z archive
31
+ #
32
+ # @param path [String] Output path
33
+ # @param options [Hash] Compression options
34
+ # @option options [Integer] :volume_size Volume size for split archives
35
+ # @option options [String] :password Password for header encryption
36
+ # @option options [Boolean] :encrypt_headers Encrypt archive headers
37
+ # @yield [writer] Block for adding files
38
+ # @yieldparam writer [Writer] Archive writer
39
+ def self.create(path, options = {})
40
+ writer = Writer.new(path, options)
41
+ yield writer if block_given?
42
+ writer.write
43
+ writer
44
+ end
45
+
46
+ # Create a split .7z archive
47
+ #
48
+ # @param path [String] Base path (e.g., "backup.7z.001")
49
+ # @param split_options [Models::SplitOptions] Split configuration
50
+ # @param options [Hash] Compression options
51
+ # @yield [writer] Block for adding files
52
+ # @yieldparam writer [SplitArchiveWriter] Archive writer
53
+ def self.create_split(path, split_options, options = {})
54
+ writer = SplitArchiveWriter.new(path, options, split_options)
55
+ yield writer if block_given?
56
+ writer.write
57
+ writer
58
+ end
59
+
60
+ # Open existing .7z archive
61
+ #
62
+ # @param path [String] Archive path
63
+ # @param options [Hash] Reader options
64
+ # @option options [String] :password Password for encrypted headers
65
+ # @yield [reader] Block for reading archive
66
+ # @yieldparam reader [Reader] Archive reader
67
+ # @return [Reader] Archive reader
68
+ def self.open(path, options = {})
69
+ reader = Reader.new(path, options)
70
+ reader.open
71
+
72
+ if block_given?
73
+ begin
74
+ yield reader
75
+ ensure
76
+ reader.split_reader&.close if reader.respond_to?(:split_reader)
77
+ end
78
+ end
79
+
80
+ reader
81
+ end
82
+
83
+ # Auto-register .7z format when loaded
84
+ def self.register!
85
+ require_relative "../format_registry"
86
+ FormatRegistry.register(".7z", Reader)
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # Auto-register on load
93
+ Omnizip::Formats::SevenZip.register!
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Formats
5
+ module Tar
6
+ # TAR format constants
7
+ module Constants
8
+ # TAR header size (POSIX ustar format)
9
+ HEADER_SIZE = 512
10
+
11
+ # Block size for TAR archives
12
+ BLOCK_SIZE = 512
13
+
14
+ # Type flags for TAR entries
15
+ TYPE_REGULAR = "0" # Regular file
16
+ TYPE_HARD_LINK = "1" # Hard link
17
+ TYPE_SYMLINK = "2" # Symbolic link
18
+ TYPE_CHAR_DEVICE = "3" # Character device
19
+ TYPE_BLOCK_DEVICE = "4" # Block device
20
+ TYPE_DIRECTORY = "5" # Directory
21
+ TYPE_FIFO = "6" # FIFO
22
+ TYPE_CONTIGUOUS = "7" # Contiguous file
23
+ TYPE_EXTENDED = "x" # Extended header
24
+ TYPE_GLOBAL_EXTENDED = "g" # Global extended header
25
+ TYPE_GNU_LONGNAME = "L" # GNU long name
26
+ TYPE_GNU_LONGLINK = "K" # GNU long link
27
+
28
+ # POSIX ustar magic value
29
+ USTAR_MAGIC = "ustar"
30
+
31
+ # POSIX ustar version
32
+ USTAR_VERSION = "00"
33
+
34
+ # Maximum file size (8GB - 1 for base-8 12-byte field)
35
+ MAX_FILE_SIZE = (8**12) - 1
36
+
37
+ # Field positions in TAR header
38
+ NAME_OFFSET = 0
39
+ NAME_SIZE = 100
40
+ MODE_OFFSET = 100
41
+ MODE_SIZE = 8
42
+ UID_OFFSET = 108
43
+ UID_SIZE = 8
44
+ GID_OFFSET = 116
45
+ GID_SIZE = 8
46
+ SIZE_OFFSET = 124
47
+ SIZE_SIZE = 12
48
+ MTIME_OFFSET = 136
49
+ MTIME_SIZE = 12
50
+ CHECKSUM_OFFSET = 148
51
+ CHECKSUM_SIZE = 8
52
+ TYPEFLAG_OFFSET = 156
53
+ TYPEFLAG_SIZE = 1
54
+ LINKNAME_OFFSET = 157
55
+ LINKNAME_SIZE = 100
56
+ MAGIC_OFFSET = 257
57
+ MAGIC_SIZE = 6
58
+ VERSION_OFFSET = 263
59
+ VERSION_SIZE = 2
60
+ UNAME_OFFSET = 265
61
+ UNAME_SIZE = 32
62
+ GNAME_OFFSET = 297
63
+ GNAME_SIZE = 32
64
+ DEVMAJOR_OFFSET = 329
65
+ DEVMAJOR_SIZE = 8
66
+ DEVMINOR_OFFSET = 337
67
+ DEVMINOR_SIZE = 8
68
+ PREFIX_OFFSET = 345
69
+ PREFIX_SIZE = 155
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+
5
+ module Omnizip
6
+ module Formats
7
+ module Tar
8
+ # TAR entry model
9
+ #
10
+ # Represents a single entry (file, directory, link) in a TAR archive
11
+ class Entry
12
+ include Constants
13
+
14
+ attr_accessor :name, :mode, :uid, :gid, :size, :mtime, :typeflag,
15
+ :linkname, :uname, :gname, :devmajor, :devminor, :prefix
16
+ attr_reader :data
17
+
18
+ # Initialize a new TAR entry
19
+ #
20
+ # @param name [String] Entry name/path
21
+ # @param options [Hash] Entry options
22
+ def initialize(name, options = {})
23
+ @name = name
24
+ @mode = options[:mode] || 0o644
25
+ @uid = options[:uid] || 0
26
+ @gid = options[:gid] || 0
27
+ @size = options[:size] || 0
28
+ @mtime = options[:mtime] || Time.now
29
+ @typeflag = options[:typeflag] || TYPE_REGULAR
30
+ @linkname = options[:linkname] || ""
31
+ @uname = options[:uname] || ""
32
+ @gname = options[:gname] || ""
33
+ @devmajor = options[:devmajor] || 0
34
+ @devminor = options[:devminor] || 0
35
+ @prefix = options[:prefix] || ""
36
+ @data = nil
37
+ end
38
+
39
+ # Set entry data
40
+ #
41
+ # @param data [String] Entry data
42
+ def data=(data)
43
+ @data = data
44
+ @size = data.bytesize if data
45
+ end
46
+
47
+ # Check if entry is a directory
48
+ #
49
+ # @return [Boolean] true if directory
50
+ def directory?
51
+ @typeflag == TYPE_DIRECTORY
52
+ end
53
+
54
+ # Check if entry is a file
55
+ #
56
+ # @return [Boolean] true if regular file
57
+ def file?
58
+ @typeflag == TYPE_REGULAR || @typeflag.nil? || @typeflag.empty?
59
+ end
60
+
61
+ # Check if entry is a symbolic link
62
+ #
63
+ # @return [Boolean] true if symbolic link
64
+ def symlink?
65
+ @typeflag == TYPE_SYMLINK
66
+ end
67
+
68
+ # Get full entry name (prefix + name)
69
+ #
70
+ # @return [String] Full entry name
71
+ def full_name
72
+ if @prefix && !@prefix.empty?
73
+ File.join(@prefix, @name)
74
+ else
75
+ @name
76
+ end
77
+ end
78
+
79
+ # Calculate checksum for TAR header
80
+ #
81
+ # @param header [String] TAR header bytes
82
+ # @return [Integer] Checksum value
83
+ def self.calculate_checksum(header)
84
+ # Replace checksum field with spaces for calculation
85
+ checksum_header = header.dup
86
+ checksum_header[CHECKSUM_OFFSET, CHECKSUM_SIZE] = " " * CHECKSUM_SIZE
87
+
88
+ # Sum all bytes
89
+ checksum_header.bytes.sum
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "entry"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Tar
9
+ # TAR header parser (POSIX ustar format)
10
+ #
11
+ # Handles reading and writing 512-byte TAR headers
12
+ class Header
13
+ include Constants
14
+
15
+ # Parse a TAR header from binary data
16
+ #
17
+ # @param header_data [String] 512 bytes of header data
18
+ # @return [Entry, nil] Parsed entry or nil if end of archive
19
+ def self.parse(header_data)
20
+ return nil if header_data.nil? || header_data.bytesize < HEADER_SIZE
21
+ return nil if all_zeros?(header_data)
22
+
23
+ entry = Entry.new("")
24
+
25
+ # Extract fields from header
26
+ entry.name = extract_string(header_data, NAME_OFFSET, NAME_SIZE)
27
+ entry.mode = extract_octal(header_data, MODE_OFFSET, MODE_SIZE)
28
+ entry.uid = extract_octal(header_data, UID_OFFSET, UID_SIZE)
29
+ entry.gid = extract_octal(header_data, GID_OFFSET, GID_SIZE)
30
+ entry.size = extract_octal(header_data, SIZE_OFFSET, SIZE_SIZE)
31
+ entry.mtime = Time.at(
32
+ extract_octal(header_data, MTIME_OFFSET, MTIME_SIZE),
33
+ )
34
+ entry.typeflag = header_data[TYPEFLAG_OFFSET]
35
+ entry.linkname = extract_string(
36
+ header_data, LINKNAME_OFFSET, LINKNAME_SIZE
37
+ )
38
+
39
+ # Check for ustar format
40
+ magic = extract_string(header_data, MAGIC_OFFSET, MAGIC_SIZE)
41
+ if magic == USTAR_MAGIC
42
+ entry.uname = extract_string(header_data, UNAME_OFFSET, UNAME_SIZE)
43
+ entry.gname = extract_string(header_data, GNAME_OFFSET, GNAME_SIZE)
44
+ entry.devmajor = extract_octal(
45
+ header_data, DEVMAJOR_OFFSET, DEVMAJOR_SIZE
46
+ )
47
+ entry.devminor = extract_octal(
48
+ header_data, DEVMINOR_OFFSET, DEVMINOR_SIZE
49
+ )
50
+ entry.prefix = extract_string(
51
+ header_data, PREFIX_OFFSET, PREFIX_SIZE
52
+ )
53
+ end
54
+
55
+ # Verify checksum
56
+ checksum = extract_octal(
57
+ header_data, CHECKSUM_OFFSET, CHECKSUM_SIZE
58
+ )
59
+ calculated = Entry.calculate_checksum(header_data)
60
+ unless checksum == calculated
61
+ raise Error, "TAR header checksum mismatch"
62
+ end
63
+
64
+ entry
65
+ end
66
+
67
+ # Build a TAR header from an entry
68
+ #
69
+ # @param entry [Entry] Entry to build header for
70
+ # @return [String] 512 bytes of header data
71
+ def self.build(entry)
72
+ header = "\0" * HEADER_SIZE
73
+
74
+ # Write fields to header
75
+ write_string(header, entry.name, NAME_OFFSET, NAME_SIZE)
76
+ write_octal(header, entry.mode, MODE_OFFSET, MODE_SIZE)
77
+ write_octal(header, entry.uid, UID_OFFSET, UID_SIZE)
78
+ write_octal(header, entry.gid, GID_OFFSET, GID_SIZE)
79
+ write_octal(header, entry.size, SIZE_OFFSET, SIZE_SIZE)
80
+ write_octal(
81
+ header, entry.mtime.to_i, MTIME_OFFSET, MTIME_SIZE
82
+ )
83
+ header[TYPEFLAG_OFFSET] = entry.typeflag || TYPE_REGULAR
84
+ write_string(header, entry.linkname, LINKNAME_OFFSET, LINKNAME_SIZE)
85
+
86
+ # Write ustar magic and version
87
+ write_string(header, USTAR_MAGIC, MAGIC_OFFSET, MAGIC_SIZE)
88
+ write_string(header, USTAR_VERSION, VERSION_OFFSET, VERSION_SIZE)
89
+ write_string(header, entry.uname, UNAME_OFFSET, UNAME_SIZE)
90
+ write_string(header, entry.gname, GNAME_OFFSET, GNAME_SIZE)
91
+ write_octal(header, entry.devmajor, DEVMAJOR_OFFSET, DEVMAJOR_SIZE)
92
+ write_octal(header, entry.devminor, DEVMINOR_OFFSET, DEVMINOR_SIZE)
93
+ write_string(header, entry.prefix, PREFIX_OFFSET, PREFIX_SIZE)
94
+
95
+ # Calculate and write checksum
96
+ checksum = Entry.calculate_checksum(header)
97
+ write_octal(header, checksum, CHECKSUM_OFFSET, CHECKSUM_SIZE)
98
+
99
+ header
100
+ end
101
+
102
+ # Check if header data is all zeros (end of archive marker)
103
+ #
104
+ # @param data [String] Header data
105
+ # @return [Boolean] true if all zeros
106
+ def self.all_zeros?(data)
107
+ data.bytes.all?(&:zero?)
108
+ end
109
+
110
+ # Extract a null-terminated string from header
111
+ #
112
+ # @param header [String] Header data
113
+ # @param offset [Integer] Field offset
114
+ # @param size [Integer] Field size
115
+ # @return [String] Extracted string
116
+ def self.extract_string(header, offset, size)
117
+ field = header[offset, size]
118
+ return "" if field.nil?
119
+
120
+ # Find null terminator
121
+ null_pos = field.index("\0")
122
+ null_pos ? field[0...null_pos] : field
123
+ end
124
+
125
+ # Extract an octal number from header
126
+ #
127
+ # @param header [String] Header data
128
+ # @param offset [Integer] Field offset
129
+ # @param size [Integer] Field size
130
+ # @return [Integer] Extracted number
131
+ def self.extract_octal(header, offset, size)
132
+ field = extract_string(header, offset, size)
133
+ field.strip.to_i(8)
134
+ end
135
+
136
+ # Write a string to header
137
+ #
138
+ # @param header [String] Header data
139
+ # @param value [String] Value to write
140
+ # @param offset [Integer] Field offset
141
+ # @param size [Integer] Field size
142
+ def self.write_string(header, value, offset, size)
143
+ value = value.to_s
144
+ # Truncate if too long
145
+ value = value[0, size - 1] if value.bytesize >= size
146
+ header[offset, value.bytesize] = value
147
+ end
148
+
149
+ # Write an octal number to header
150
+ #
151
+ # @param header [String] Header data
152
+ # @param value [Integer] Value to write
153
+ # @param offset [Integer] Field offset
154
+ # @param size [Integer] Field size
155
+ def self.write_octal(header, value, offset, size)
156
+ # Format as octal with leading zeros
157
+ octal_str = format("%0#{size - 1}o", value.to_i)
158
+ # Truncate if too long
159
+ octal_str = octal_str[(-size + 1)..] if octal_str.bytesize >= size
160
+ header[offset, octal_str.bytesize] = octal_str
161
+ end
162
+
163
+ private_class_method :extract_string, :extract_octal
164
+ private_class_method :write_string, :write_octal, :all_zeros?
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "header"
5
+ require_relative "entry"
6
+
7
+ module Omnizip
8
+ module Formats
9
+ module Tar
10
+ # TAR archive reader
11
+ #
12
+ # Reads and extracts TAR archives
13
+ class Reader
14
+ include Constants
15
+
16
+ attr_reader :file_path, :entries
17
+
18
+ # Initialize TAR reader
19
+ #
20
+ # @param file_path [String] Path to TAR archive
21
+ def initialize(file_path)
22
+ @file_path = file_path
23
+ @entries = []
24
+ @file = nil
25
+ end
26
+
27
+ # Read TAR archive and parse all entries
28
+ #
29
+ # @return [self] Returns self for method chaining
30
+ def read
31
+ File.open(@file_path, "rb") do |file|
32
+ @file = file
33
+ parse_entries
34
+ end
35
+ self
36
+ end
37
+
38
+ # Extract all entries to a directory
39
+ #
40
+ # @param output_dir [String] Output directory path
41
+ def extract_all(output_dir)
42
+ read unless @entries.any?
43
+
44
+ FileUtils.mkdir_p(output_dir)
45
+
46
+ @entries.each do |entry|
47
+ extract_entry(entry, output_dir)
48
+ end
49
+ end
50
+
51
+ # Extract a specific entry
52
+ #
53
+ # @param entry [Entry] Entry to extract
54
+ # @param output_dir [String] Output directory
55
+ def extract_entry(entry, output_dir)
56
+ full_path = File.join(output_dir, entry.full_name)
57
+
58
+ if entry.directory?
59
+ FileUtils.mkdir_p(full_path)
60
+ elsif entry.file?
61
+ FileUtils.mkdir_p(File.dirname(full_path))
62
+ File.binwrite(full_path, entry.data)
63
+ File.chmod(entry.mode, full_path) if entry.mode
64
+ File.utime(entry.mtime, entry.mtime, full_path) if entry.mtime
65
+ elsif entry.symlink?
66
+ FileUtils.mkdir_p(File.dirname(full_path))
67
+ File.symlink(entry.linkname, full_path)
68
+ end
69
+ end
70
+
71
+ # List all entries
72
+ #
73
+ # @return [Array<Entry>] List of entries
74
+ def list_entries
75
+ read unless @entries.any?
76
+ @entries
77
+ end
78
+
79
+ # Open TAR archive and yield reader
80
+ #
81
+ # @param file_path [String] Path to TAR archive
82
+ # @yield [Reader] Reader instance
83
+ def self.open(file_path)
84
+ reader = new(file_path)
85
+ reader.read
86
+ yield reader if block_given?
87
+ reader
88
+ end
89
+
90
+ private
91
+
92
+ # Parse all entries from TAR archive
93
+ def parse_entries
94
+ @entries = []
95
+
96
+ loop do
97
+ header_data = @file.read(HEADER_SIZE)
98
+ break if header_data.nil? || header_data.bytesize < HEADER_SIZE
99
+
100
+ entry = Header.parse(header_data)
101
+ break if entry.nil?
102
+
103
+ # Read entry data
104
+ if entry.size.positive?
105
+ entry.data = @file.read(entry.size)
106
+
107
+ # Skip to next block boundary
108
+ remainder = entry.size % BLOCK_SIZE
109
+ if remainder.positive?
110
+ padding = BLOCK_SIZE - remainder
111
+ @file.read(padding)
112
+ end
113
+ end
114
+
115
+ @entries << entry
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end