omnizip 0.3.8 → 0.3.10

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +51 -50
  3. data/Gemfile +2 -0
  4. data/Rakefile +3 -0
  5. data/TODO.refactor/00-overview.md +67 -0
  6. data/TODO.refactor/01-registry-base-class.md +81 -0
  7. data/TODO.refactor/02-error-hierarchy.md +57 -0
  8. data/TODO.refactor/03-autoload-entry-point.md +69 -0
  9. data/TODO.refactor/04-filter-base-consolidation.md +49 -0
  10. data/TODO.refactor/05-send-private-methods.md +64 -0
  11. data/TODO.refactor/06-instance-variable-access.md +69 -0
  12. data/TODO.refactor/07-respond-to-replacement.md +91 -0
  13. data/TODO.refactor/08-spec-doubles.md +27 -0
  14. data/TODO.refactor/09-cli-shared-module.md +35 -0
  15. data/TODO.refactor/10-format-detector-ocp.md +34 -0
  16. data/TODO.refactor/11-convenience-decoupling.md +38 -0
  17. data/TODO.refactor/12-thread-safety.md +8 -0
  18. data/TODO.refactor/13-lutaml-model-migration.md +82 -0
  19. data/TODO.refactor/14-add-missing-specs.md +25 -0
  20. data/lib/omnizip/algorithm.rb +0 -2
  21. data/lib/omnizip/algorithm_registry.rb +29 -57
  22. data/lib/omnizip/algorithms/lzma/bit_model.rb +1 -1
  23. data/lib/omnizip/algorithms/lzma/dictionary.rb +4 -3
  24. data/lib/omnizip/algorithms/lzma/distance_coder.rb +5 -102
  25. data/lib/omnizip/algorithms/lzma/length_coder.rb +4 -92
  26. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +4 -119
  27. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +2 -0
  28. data/lib/omnizip/algorithms/lzma/match_finder.rb +59 -29
  29. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +20 -12
  30. data/lib/omnizip/algorithms/lzma/range_decoder.rb +77 -269
  31. data/lib/omnizip/algorithms/lzma/range_encoder.rb +0 -9
  32. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +0 -15
  33. data/lib/omnizip/algorithms/lzma/xz_state.rb +0 -8
  34. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +189 -968
  35. data/lib/omnizip/algorithms/lzma2/encoder.rb +0 -2
  36. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +0 -2
  37. data/lib/omnizip/algorithms/lzma2/properties.rb +0 -2
  38. data/lib/omnizip/algorithms/ppmd7/model.rb +1 -1
  39. data/lib/omnizip/algorithms/ppmd8/context.rb +0 -2
  40. data/lib/omnizip/algorithms/ppmd8/model.rb +1 -1
  41. data/lib/omnizip/algorithms/ppmd8.rb +0 -2
  42. data/lib/omnizip/algorithms/ppmd_base.rb +0 -2
  43. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +0 -5
  44. data/lib/omnizip/algorithms/xz_lzma2.rb +0 -5
  45. data/lib/omnizip/algorithms/zstandard/frame/header.rb +3 -5
  46. data/lib/omnizip/algorithms.rb +1 -0
  47. data/lib/omnizip/archive_handler.rb +75 -0
  48. data/lib/omnizip/archive_handlers/tar_handler.rb +51 -0
  49. data/lib/omnizip/archive_handlers/zip_handler.rb +73 -0
  50. data/lib/omnizip/archive_handlers.rb +11 -0
  51. data/lib/omnizip/checksum_registry.rb +23 -96
  52. data/lib/omnizip/cli/shared.rb +31 -0
  53. data/lib/omnizip/cli.rb +6 -43
  54. data/lib/omnizip/commands/archive_create_command.rb +0 -6
  55. data/lib/omnizip/commands/archive_extract_command.rb +0 -4
  56. data/lib/omnizip/commands/archive_list_command.rb +0 -4
  57. data/lib/omnizip/commands/archive_repair_command.rb +0 -2
  58. data/lib/omnizip/commands/archive_verify_command.rb +0 -2
  59. data/lib/omnizip/commands/compress_command.rb +0 -2
  60. data/lib/omnizip/commands/decompress_command.rb +0 -2
  61. data/lib/omnizip/commands/list_command.rb +0 -2
  62. data/lib/omnizip/commands/metadata_command.rb +0 -5
  63. data/lib/omnizip/commands/parity_create_command.rb +0 -2
  64. data/lib/omnizip/commands/parity_repair_command.rb +0 -2
  65. data/lib/omnizip/commands/parity_verify_command.rb +0 -2
  66. data/lib/omnizip/commands/profile_list_command.rb +0 -2
  67. data/lib/omnizip/commands/profile_show_command.rb +0 -2
  68. data/lib/omnizip/convenience.rb +111 -234
  69. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +1 -1
  70. data/lib/omnizip/crypto/aes256/cipher.rb +6 -1
  71. data/lib/omnizip/error.rb +22 -18
  72. data/lib/omnizip/filter.rb +5 -3
  73. data/lib/omnizip/filter_registry.rb +74 -98
  74. data/lib/omnizip/filters/bcj2/decoder.rb +196 -130
  75. data/lib/omnizip/filters/bcj2.rb +0 -2
  76. data/lib/omnizip/filters/bcj_x86.rb +0 -2
  77. data/lib/omnizip/filters/filter_base.rb +4 -70
  78. data/lib/omnizip/filters/registry.rb +0 -2
  79. data/lib/omnizip/format_detector.rb +21 -12
  80. data/lib/omnizip/format_registry.rb +79 -38
  81. data/lib/omnizip/formats/bzip2_file.rb +0 -2
  82. data/lib/omnizip/formats/cpio/entry.rb +0 -2
  83. data/lib/omnizip/formats/cpio/reader.rb +0 -1
  84. data/lib/omnizip/formats/cpio/writer.rb +0 -1
  85. data/lib/omnizip/formats/cpio.rb +0 -1
  86. data/lib/omnizip/formats/format_spec_loader.rb +1 -1
  87. data/lib/omnizip/formats/gzip.rb +0 -2
  88. data/lib/omnizip/formats/iso/directory_record.rb +6 -3
  89. data/lib/omnizip/formats/iso/reader.rb +2 -2
  90. data/lib/omnizip/formats/iso.rb +0 -1
  91. data/lib/omnizip/formats/lzip.rb +5 -25
  92. data/lib/omnizip/formats/lzma_alone.rb +7 -25
  93. data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
  94. data/lib/omnizip/formats/msi/constants.rb +157 -0
  95. data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
  96. data/lib/omnizip/formats/msi/entry.rb +128 -0
  97. data/lib/omnizip/formats/msi/reader.rb +301 -0
  98. data/lib/omnizip/formats/msi/string_pool.rb +246 -0
  99. data/lib/omnizip/formats/msi/table_parser.rb +244 -0
  100. data/lib/omnizip/formats/msi.rb +84 -0
  101. data/lib/omnizip/formats/ole/allocation_table.rb +10 -1
  102. data/lib/omnizip/formats/ole/dirent.rb +20 -3
  103. data/lib/omnizip/formats/ole/header.rb +0 -1
  104. data/lib/omnizip/formats/ole/ranges_io.rb +21 -3
  105. data/lib/omnizip/formats/ole/storage.rb +19 -4
  106. data/lib/omnizip/formats/ole.rb +0 -2
  107. data/lib/omnizip/formats/rar/block_parser.rb +0 -1
  108. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +1 -1
  109. data/lib/omnizip/formats/rar/decompressor.rb +38 -1
  110. data/lib/omnizip/formats/rar/header.rb +0 -1
  111. data/lib/omnizip/formats/rar/rar5/writer.rb +0 -2
  112. data/lib/omnizip/formats/rar/reader.rb +2 -3
  113. data/lib/omnizip/formats/rar/writer.rb +0 -1
  114. data/lib/omnizip/formats/rar.rb +2 -2
  115. data/lib/omnizip/formats/rpm/entry.rb +0 -1
  116. data/lib/omnizip/formats/rpm/header.rb +3 -8
  117. data/lib/omnizip/formats/rpm/lead.rb +37 -26
  118. data/lib/omnizip/formats/rpm/tag.rb +0 -1
  119. data/lib/omnizip/formats/rpm/writer.rb +0 -1
  120. data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +98 -61
  121. data/lib/omnizip/formats/seven_zip/coder_chain.rb +1 -2
  122. data/lib/omnizip/formats/seven_zip/encoded_header.rb +0 -1
  123. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +0 -1
  124. data/lib/omnizip/formats/seven_zip/header.rb +0 -1
  125. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +0 -1
  126. data/lib/omnizip/formats/seven_zip/header_writer.rb +22 -61
  127. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +1 -1
  128. data/lib/omnizip/formats/seven_zip/parser.rb +14 -13
  129. data/lib/omnizip/formats/seven_zip/reader.rb +82 -19
  130. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +0 -15
  131. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +0 -1
  132. data/lib/omnizip/formats/seven_zip/writer.rb +22 -68
  133. data/lib/omnizip/formats/seven_zip.rb +63 -5
  134. data/lib/omnizip/formats/tar/entry.rb +0 -2
  135. data/lib/omnizip/formats/tar/header.rb +0 -2
  136. data/lib/omnizip/formats/tar/reader.rb +0 -2
  137. data/lib/omnizip/formats/tar/writer.rb +0 -2
  138. data/lib/omnizip/formats/tar.rb +0 -1
  139. data/lib/omnizip/formats/xar/entry.rb +0 -1
  140. data/lib/omnizip/formats/xar/header.rb +0 -1
  141. data/lib/omnizip/formats/xar/reader.rb +0 -1
  142. data/lib/omnizip/formats/xar/toc.rb +3 -3
  143. data/lib/omnizip/formats/xar/writer.rb +11 -3
  144. data/lib/omnizip/formats/xar.rb +0 -1
  145. data/lib/omnizip/formats/xz/reader.rb +7 -3
  146. data/lib/omnizip/formats/xz.rb +3 -40
  147. data/lib/omnizip/formats/xz_impl/block_decoder.rb +4 -10
  148. data/lib/omnizip/formats/xz_impl/block_encoder.rb +0 -2
  149. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +0 -2
  150. data/lib/omnizip/formats/xz_impl/index_decoder.rb +0 -1
  151. data/lib/omnizip/formats/xz_impl/index_encoder.rb +0 -1
  152. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +1 -1
  153. data/lib/omnizip/formats/xz_impl/stream_footer.rb +0 -1
  154. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +0 -1
  155. data/lib/omnizip/formats/xz_impl/stream_header.rb +0 -1
  156. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +0 -2
  157. data/lib/omnizip/formats/zip/central_directory_header.rb +2 -3
  158. data/lib/omnizip/formats/zip/end_of_central_directory.rb +0 -2
  159. data/lib/omnizip/formats/zip/local_file_header.rb +0 -2
  160. data/lib/omnizip/formats/zip/reader.rb +12 -2
  161. data/lib/omnizip/formats/zip/writer.rb +73 -1
  162. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +0 -2
  163. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +0 -2
  164. data/lib/omnizip/formats/zip/zip64_extra_field.rb +0 -2
  165. data/lib/omnizip/formats/zip.rb +0 -1
  166. data/lib/omnizip/formats.rb +9 -22
  167. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +2 -2
  168. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +1 -1
  169. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +0 -6
  170. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +18 -185
  171. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +3 -3
  172. data/lib/omnizip/io/source.rb +136 -0
  173. data/lib/omnizip/io.rb +2 -0
  174. data/lib/omnizip/link_handler.rb +7 -3
  175. data/lib/omnizip/metadata/metadata_editor.rb +1 -1
  176. data/lib/omnizip/models/algorithm_metadata.rb +33 -60
  177. data/lib/omnizip/models/compression_options.rb +40 -53
  178. data/lib/omnizip/models/conversion_options.rb +9 -18
  179. data/lib/omnizip/models/parallel_options.rb +25 -0
  180. data/lib/omnizip/optimization_registry.rb +8 -44
  181. data/lib/omnizip/parallel/job_scheduler.rb +26 -8
  182. data/lib/omnizip/parallel/parallel_compressor.rb +10 -20
  183. data/lib/omnizip/parallel/parallel_extractor.rb +20 -11
  184. data/lib/omnizip/parity/par2_repairer.rb +5 -5
  185. data/lib/omnizip/parity/par2_verifier.rb +27 -22
  186. data/lib/omnizip/parity.rb +4 -4
  187. data/lib/omnizip/password/encryption_registry.rb +11 -50
  188. data/lib/omnizip/password/zip_crypto_strategy.rb +1 -4
  189. data/lib/omnizip/pipe/stream_decompressor.rb +2 -3
  190. data/lib/omnizip/profile/archive_profile.rb +0 -2
  191. data/lib/omnizip/profile/binary_profile.rb +0 -2
  192. data/lib/omnizip/profile/compression_profile.rb +0 -1
  193. data/lib/omnizip/profile/profile_detector.rb +0 -2
  194. data/lib/omnizip/profile/text_profile.rb +0 -2
  195. data/lib/omnizip/profile.rb +0 -2
  196. data/lib/omnizip/registry.rb +112 -0
  197. data/lib/omnizip/version.rb +1 -1
  198. data/lib/omnizip/zip/file.rb +20 -9
  199. data/lib/omnizip.rb +26 -55
  200. data/lib/tasks/msi_test_files.rake +66 -0
  201. metadata +57 -6
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/algorithms"
4
-
5
3
  module Omnizip
6
4
  module Algorithms
7
5
  # LZMA2 encoder - delegates to XzLZMA2Encoder
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/algorithms"
4
-
5
3
  module Omnizip
6
4
  module Algorithms
7
5
  class LZMA2 < Algorithm
@@ -20,8 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithms"
24
-
25
23
  module Omnizip
26
24
  module Algorithms
27
25
  class LZMA2
@@ -36,7 +36,7 @@ module Omnizip
36
36
  class Model
37
37
  include Constants
38
38
 
39
- attr_reader :max_order, :root_context, :current_context
39
+ attr_reader :max_order, :root_context, :current_context, :mem_size
40
40
 
41
41
  # Initialize the PPMd7 model
42
42
  #
@@ -2,8 +2,6 @@
2
2
 
3
3
  # Copyright (C) 2025 Ribose Inc.
4
4
 
5
- require "omnizip/algorithms/ppmd7/context"
6
-
7
5
  module Omnizip
8
6
  module Algorithms
9
7
  class PPMd8 < PPMdBase
@@ -35,7 +35,7 @@ module Omnizip
35
35
  include Constants
36
36
 
37
37
  attr_reader :max_order, :root_context, :current_context,
38
- :restoration_method, :run_length, :glue_count
38
+ :restoration_method, :run_length, :glue_count, :mem_size
39
39
  attr_accessor :order_fall, :prev_success
40
40
 
41
41
  # Initialize the PPMd8 model
@@ -20,8 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithms"
24
-
25
23
  module Omnizip
26
24
  module Algorithms
27
25
  # PPMd8 (PPMdI) compression algorithm
@@ -20,8 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithm"
24
-
25
23
  module Omnizip
26
24
  module Algorithms
27
25
  # Base class for PPMd (Prediction by Partial Matching) algorithms
@@ -20,11 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithm"
24
- require "omnizip/models/algorithm_metadata"
25
- require "omnizip/algorithms/lzma2/encoder"
26
- require "omnizip/algorithms/lzma2/decoder"
27
-
28
23
  module Omnizip
29
24
  module Algorithms
30
25
  # 7-Zip LZMA2 compression algorithm.
@@ -20,11 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithm"
24
- require "omnizip/models/algorithm_metadata"
25
- require "omnizip/implementations/xz_utils/lzma2/encoder"
26
- require "omnizip/implementations/xz_utils/lzma2/decoder"
27
-
28
23
  module Omnizip
29
24
  module Algorithms
30
25
  # XZ Utils LZMA2 compression algorithm.
@@ -69,15 +69,15 @@ module Omnizip
69
69
 
70
70
  # Parse optional fields based on descriptor bits
71
71
  if header.window_descriptor?
72
- header.send(:parse_window_descriptor, input)
72
+ header.parse_window_descriptor(input)
73
73
  end
74
74
 
75
75
  if header.dictionary_id?
76
- header.send(:parse_dictionary_id, input)
76
+ header.parse_dictionary_id(input)
77
77
  end
78
78
 
79
79
  if header.content_size?
80
- header.send(:parse_content_size, input)
80
+ header.parse_content_size(input)
81
81
  end
82
82
 
83
83
  header
@@ -174,8 +174,6 @@ module Omnizip
174
174
  (1 << @window_log) + (mantissa << (@window_log - 4))
175
175
  end
176
176
 
177
- private
178
-
179
177
  # Parse window descriptor byte
180
178
  def parse_window_descriptor(input)
181
179
  byte = input.read(1).ord
@@ -3,6 +3,7 @@
3
3
  module Omnizip
4
4
  # Compression algorithm implementations
5
5
  module Algorithms
6
+ autoload :PPMdBase, "omnizip/algorithms/ppmd_base"
6
7
  autoload :LZMA2Const, "omnizip/algorithms/lzma2/constants"
7
8
  autoload :LZMA, "omnizip/algorithms/lzma"
8
9
  autoload :LZMA2, "omnizip/algorithms/lzma2"
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ # Unified archive handler interface for the Convenience module.
5
+ #
6
+ # Each format that wants to participate in Omnizip.compress_file /
7
+ # extract_archive / etc. registers a Handler here. The Handler exposes
8
+ # a small canonical API (+create+, +open+, +extract_to+, +list+) that
9
+ # the Convenience module calls; the Handler then delegates to the
10
+ # format-specific reader/writer.
11
+ #
12
+ # @example Register a format
13
+ # Omnizip::ArchiveHandler.register(:tar, TarHandler.new)
14
+ #
15
+ # @example Use from a caller
16
+ # Omnizip::ArchiveHandler.for(:tar).create(path) { |w| w.add_file(...) }
17
+ module ArchiveHandler
18
+ @handlers = {}
19
+
20
+ class << self
21
+ # Register a handler for a format symbol.
22
+ #
23
+ # @param format [Symbol] e.g. +:zip+, +:tar+
24
+ # @param handler [Object] responding to the Handler interface
25
+ # @return [void]
26
+ def register(format, handler)
27
+ @handlers[format.to_sym] = handler
28
+ end
29
+
30
+ # Look up the handler for +format+. Triggers the registered lazy
31
+ # load constant if the format is one of the built-ins.
32
+ #
33
+ # @param format [Symbol]
34
+ # @return [Object]
35
+ # @raise [Omnizip::UnsupportedFormatError] if +format+ is unknown
36
+ def for(format)
37
+ handler = @handlers[format.to_sym]
38
+ return handler if handler
39
+
40
+ trigger = LAZY_LOAD_TRIGGERS[format.to_sym]
41
+ if trigger
42
+ trigger.call
43
+ handler = @handlers[format.to_sym]
44
+ return handler if handler
45
+ end
46
+
47
+ raise Omnizip::UnsupportedFormatError,
48
+ "No archive handler registered for #{format.inspect}. " \
49
+ "Registered: #{@handlers.keys.join(', ')}"
50
+ end
51
+
52
+ # List of registered format names.
53
+ #
54
+ # @return [Array<Symbol>]
55
+ def available
56
+ @handlers.keys
57
+ end
58
+
59
+ # Remove a handler (primarily for testing).
60
+ #
61
+ # @return [void]
62
+ def unregister(format)
63
+ @handlers.delete(format.to_sym)
64
+ end
65
+ end
66
+
67
+ # Lazy triggers — referencing the constant autoloads the handler's
68
+ # file, which then self-registers at the bottom of the file.
69
+ LAZY_LOAD_TRIGGERS = {
70
+ zip: -> { Omnizip::ArchiveHandlers::ZipHandler },
71
+ tar: -> { Omnizip::ArchiveHandlers::TarHandler },
72
+ }.freeze
73
+ private_constant :LAZY_LOAD_TRIGGERS
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module ArchiveHandlers
5
+ # Adapter that exposes the canonical +create+/+open+/+extract_to+
6
+ # /+list+ interface for the TAR format. Wraps +Omnizip::Formats::Tar+.
7
+ class TarHandler
8
+ def create(path, &block)
9
+ Omnizip::Formats::Tar.create(path, &block)
10
+ end
11
+
12
+ def open(path, &block)
13
+ Omnizip::Formats::Tar.open(path, &block)
14
+ end
15
+
16
+ def extract_to(path, output_dir, **_)
17
+ Omnizip::Formats::Tar.extract(path, output_dir)
18
+ end
19
+
20
+ def list(path, details: false, **_)
21
+ entries = Omnizip::Formats::Tar.list(path)
22
+ if details
23
+ entries.map do |entry|
24
+ {
25
+ name: entry.name,
26
+ size: entry.size,
27
+ directory: entry.directory?,
28
+ mtime: entry.mtime,
29
+ mode: entry.mode,
30
+ }
31
+ end
32
+ else
33
+ entries.map(&:name)
34
+ end
35
+ end
36
+
37
+ def read_entry(path, entry_name)
38
+ data = nil
39
+ Omnizip::Formats::Tar.open(path) do |reader|
40
+ entry = reader.entries.find { |e| e.name == entry_name }
41
+ raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry
42
+
43
+ data = entry.data
44
+ end
45
+ data
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ Omnizip::ArchiveHandler.register(:tar, Omnizip::ArchiveHandlers::TarHandler.new)
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module ArchiveHandlers
5
+ # Adapter that exposes the canonical +create+/+open+/+extract_to+
6
+ # /+list+ interface for the ZIP format. Wraps +Omnizip::Zip::File+.
7
+ class ZipHandler
8
+ def create(path, &block)
9
+ Omnizip::Zip::File.create(path, &block)
10
+ end
11
+
12
+ def open(path, &block)
13
+ Omnizip::Zip::File.open(path, &block)
14
+ end
15
+
16
+ def extract_to(path, output_dir, overwrite: false, **_)
17
+ extracted = []
18
+ Omnizip::Zip::File.open(path) do |zip|
19
+ zip.each do |entry|
20
+ dest_path = ::File.join(output_dir, entry.name)
21
+ on_exists = if overwrite
22
+ proc { true }
23
+ else
24
+ proc { |_e, p| raise "File exists: #{p}" }
25
+ end
26
+ zip.extract(entry, dest_path, &on_exists)
27
+ extracted << dest_path
28
+ end
29
+ end
30
+ extracted
31
+ end
32
+
33
+ def list(path, details: false, **_)
34
+ Omnizip::Zip::File.open(path) do |zip|
35
+ if details
36
+ zip.entries.map do |entry|
37
+ {
38
+ name: entry.name,
39
+ size: entry.size,
40
+ compressed_size: entry.compressed_size,
41
+ compression_method: entry.compression_method,
42
+ crc: entry.crc,
43
+ time: entry.time,
44
+ directory: entry.directory?,
45
+ }
46
+ end
47
+ else
48
+ zip.names
49
+ end
50
+ end
51
+ end
52
+
53
+ def read_entry(path, entry_name)
54
+ Omnizip::Zip::File.open(path) do |zip|
55
+ entry = zip.get_entry(entry_name)
56
+ raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry
57
+
58
+ zip.read(entry)
59
+ end
60
+ end
61
+
62
+ def add_entry(path, entry_name, source_path)
63
+ Omnizip::Zip::File.open(path) { |zip| zip.add(entry_name, source_path) }
64
+ end
65
+
66
+ def remove_entry(path, entry_name)
67
+ Omnizip::Zip::File.open(path) { |zip| zip.remove(entry_name) }
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ Omnizip::ArchiveHandler.register(:zip, Omnizip::ArchiveHandlers::ZipHandler.new)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ # Archive handler namespace. Each format ships a Handler class here
5
+ # that adapts its format-specific API to the canonical contract
6
+ # documented on +Omnizip::ArchiveHandler+.
7
+ module ArchiveHandlers
8
+ autoload :ZipHandler, "omnizip/archive_handlers/zip_handler"
9
+ autoload :TarHandler, "omnizip/archive_handlers/tar_handler"
10
+ end
11
+ end
@@ -1,114 +1,41 @@
1
1
  # frozen_string_literal: true
2
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
3
  module Omnizip
20
- # Registry for managing available checksum algorithms.
21
- #
22
- # This class maintains a central registry of all checksum implementations
23
- # available in Omnizip, providing a consistent interface for algorithm
24
- # discovery and instantiation.
25
- #
26
- # The registry pattern ensures that checksum algorithms are defined in
27
- # one place and can be easily extended with new implementations without
28
- # modifying existing code.
29
- #
30
- # @example Register a new checksum algorithm
31
- # ChecksumRegistry.register(:crc32, Omnizip::Checksums::Crc32)
32
- #
33
- # @example Get a checksum class
34
- # crc_class = ChecksumRegistry.get(:crc32)
35
- # checksum = crc_class.calculate("data")
36
- #
37
- # @example List available checksums
38
- # ChecksumRegistry.available
39
- # # => [:crc32, :crc64]
40
- class ChecksumRegistry
41
- @checksums = {}
4
+ class ChecksumRegistry < Omnizip::Registry
5
+ BUILTIN_CHECKSUMS = {
6
+ crc32: "Omnizip::Checksums::Crc32",
7
+ crc64: "Omnizip::Checksums::Crc64",
8
+ }.freeze
42
9
 
43
10
  class << self
44
- # Register a checksum algorithm.
45
- #
46
- # Adds a new checksum implementation to the registry, making it
47
- # available for use throughout the application.
48
- #
49
- # @param name [Symbol] unique identifier for the checksum
50
- # @param checksum_class [Class] class implementing the checksum
51
- # @return [void]
52
- # @raise [ArgumentError] if name is already registered
53
- def register(name, checksum_class)
54
- name = name.to_sym
55
- if @checksums.key?(name)
56
- raise ArgumentError,
57
- "Checksum '#{name}' is already registered"
58
- end
59
-
60
- @checksums[name] = checksum_class
11
+ def not_found_error_class
12
+ Omnizip::UnknownChecksumError
61
13
  end
62
14
 
63
- # Retrieve a checksum class by name.
64
- #
65
- # Returns the checksum class registered under the given name,
66
- # allowing for instantiation and use.
67
- #
68
- # @param name [Symbol] identifier of the checksum to retrieve
69
- # @return [Class] the checksum class
70
- # @raise [UnknownAlgorithmError] if checksum not found
71
- def get(name)
72
- name = name.to_sym
73
- checksum_class = @checksums[name]
15
+ def label
16
+ "Checksum"
17
+ end
74
18
 
75
- unless checksum_class
76
- raise UnknownAlgorithmError,
77
- "Unknown checksum: '#{name}'. Available: " \
78
- "#{available.join(', ')}"
19
+ def register(name, checksum_class)
20
+ key = normalize_key(name)
21
+ existing = storage[key]
22
+ if existing && existing != checksum_class
23
+ raise ArgumentError, "Checksum '#{key}' is already registered"
79
24
  end
80
25
 
26
+ synchronize { storage[key] = checksum_class }
81
27
  checksum_class
82
28
  end
83
29
 
84
- # List all available checksum algorithms.
85
- #
86
- # Returns an array of registered checksum names, useful for
87
- # displaying options to users or validating input.
88
- #
89
- # @return [Array<Symbol>] array of registered checksum names
90
30
  def available
91
- @checksums.keys.sort
92
- end
93
-
94
- # Check if a checksum is registered.
95
- #
96
- # @param name [Symbol] identifier of the checksum to check
97
- # @return [Boolean] true if registered, false otherwise
98
- def registered?(name)
99
- @checksums.key?(name.to_sym)
100
- end
101
-
102
- # Clear all registered checksums.
103
- #
104
- # This method is primarily for testing purposes and should not
105
- # be used in production code.
106
- #
107
- # @return [void]
108
- # @api private
109
- def clear
110
- @checksums.clear
31
+ super.sort
111
32
  end
112
33
  end
113
34
  end
114
35
  end
36
+
37
+ Omnizip::ChecksumRegistry::BUILTIN_CHECKSUMS.each do |name, const_path|
38
+ Omnizip::ChecksumRegistry.register_lazy(name) do
39
+ Omnizip::ChecksumRegistry.register(name, Object.const_get(const_path))
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ class Cli
5
+ # Shared helpers used by the Thor command groups defined in
6
+ # lib/omnizip/cli.rb (ProfileCommands, ArchiveCommands, Cli).
7
+ module Shared
8
+ # Print a formatted error message and exit nonzero.
9
+ #
10
+ # @param error [StandardError] the error to display
11
+ def handle_error(error)
12
+ warn Omnizip::CliOutputFormatter.format_error(error)
13
+ exit 1
14
+ end
15
+
16
+ # Format a byte count with a binary-unit suffix.
17
+ #
18
+ # @param bytes [Integer] the byte count
19
+ # @return [String] human-readable size (e.g. "1.5 MB")
20
+ def format_bytes(bytes)
21
+ return "0 B" if bytes.zero?
22
+
23
+ units = %w[B KB MB GB TB]
24
+ exp = (Math.log(bytes) / Math.log(1024)).to_i
25
+ exp = [exp, units.size - 1].min
26
+
27
+ "%.1f %s" % [bytes.to_f / (1024**exp), units[exp]]
28
+ end
29
+ end
30
+ end
31
+ end
data/lib/omnizip/cli.rb CHANGED
@@ -17,22 +17,12 @@
17
17
  #
18
18
 
19
19
  require "thor"
20
- require "omnizip/commands/compress_command"
21
- require "omnizip/commands/decompress_command"
22
- require "omnizip/commands/list_command"
23
- require "omnizip/commands/archive_create_command"
24
- require "omnizip/commands/archive_extract_command"
25
- require "omnizip/commands/archive_list_command"
26
- require "omnizip/commands/profile_list_command"
27
- require "omnizip/commands/profile_show_command"
28
- require "omnizip/commands/metadata_command"
29
- require "omnizip/commands/archive_verify_command"
30
- require "omnizip/commands/archive_repair_command"
31
- require "omnizip/cli/output_formatter"
32
20
 
33
21
  module Omnizip
34
22
  # Profile commands subcommand group
35
23
  class ProfileCommands < Thor
24
+ include Omnizip::Cli::Shared
25
+
36
26
  class << self
37
27
  def exit_on_failure?
38
28
  true
@@ -75,17 +65,12 @@ module Omnizip
75
65
  rescue StandardError => e
76
66
  handle_error(e)
77
67
  end
78
-
79
- private
80
-
81
- def handle_error(error)
82
- warn Omnizip::CliOutputFormatter.format_error(error)
83
- exit 1
84
- end
85
68
  end
86
69
 
87
70
  # Archive commands subcommand group
88
71
  class ArchiveCommands < Thor
72
+ include Omnizip::Cli::Shared
73
+
89
74
  class << self
90
75
  def exit_on_failure?
91
76
  true
@@ -306,13 +291,6 @@ module Omnizip
306
291
  rescue StandardError => e
307
292
  handle_error(e)
308
293
  end
309
-
310
- private
311
-
312
- def handle_error(error)
313
- warn Omnizip::CliOutputFormatter.format_error(error)
314
- exit 1
315
- end
316
294
  end
317
295
 
318
296
  # Command-line interface for Omnizip.
@@ -320,6 +298,8 @@ module Omnizip
320
298
  # Provides Thor-based CLI commands for compressing and decompressing
321
299
  # files using various compression algorithms.
322
300
  class Cli < Thor
301
+ include Omnizip::Cli::Shared
302
+
323
303
  class << self
324
304
  def exit_on_failure?
325
305
  true
@@ -472,8 +452,6 @@ module Omnizip
472
452
  aliases: "-v",
473
453
  desc: "Enable verbose output"
474
454
  def convert(source, target)
475
- require "omnizip/converter"
476
-
477
455
  puts "Converting #{source} to #{target}..." if options[:verbose]
478
456
 
479
457
  result = Omnizip::Converter.convert(
@@ -553,20 +531,5 @@ module Omnizip
553
531
  ensure
554
532
  input_io.close if input_io && input != "-"
555
533
  end
556
-
557
- def handle_error(error)
558
- warn CliOutputFormatter.format_error(error)
559
- exit 1
560
- end
561
-
562
- def format_bytes(bytes)
563
- return "0 B" if bytes.zero?
564
-
565
- units = %w[B KB MB GB TB]
566
- exp = (Math.log(bytes) / Math.log(1024)).to_i
567
- exp = [exp, units.size - 1].min
568
-
569
- "%.1f %s" % [bytes.to_f / (1024**exp), units[exp]]
570
- end
571
534
  end
572
535
  end
@@ -16,9 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/cli/output_formatter"
20
- require "omnizip/formats/seven_zip/writer"
21
-
22
19
  module Omnizip
23
20
  module Commands
24
21
  # Command to create .7z archives from files and directories.
@@ -123,8 +120,6 @@ module Omnizip
123
120
 
124
121
  start_time = Time.now
125
122
 
126
- require "omnizip/formats/rar"
127
-
128
123
  writer_opts = {
129
124
  version: version,
130
125
  compression: compression,
@@ -388,7 +383,6 @@ module Omnizip
388
383
  def parse_volume_size(size_str)
389
384
  return nil if size_str.nil? || size_str.empty?
390
385
 
391
- require "omnizip/models/split_options"
392
386
  Omnizip::Models::SplitOptions.parse_volume_size(size_str)
393
387
  end
394
388
 
@@ -16,10 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/cli/output_formatter"
20
- require "omnizip/formats/seven_zip/reader"
21
- require "omnizip/extraction"
22
-
23
19
  module Omnizip
24
20
  module Commands
25
21
  # Command to extract .7z archives to a directory.
@@ -16,10 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/cli/output_formatter"
20
- require "omnizip/formats/seven_zip/reader"
21
- require "omnizip/extraction"
22
-
23
19
  module Omnizip
24
20
  module Commands
25
21
  # Command to list contents of .7z archives.
@@ -18,8 +18,6 @@ module Omnizip
18
18
  # @param input_path [String] Path to corrupted archive
19
19
  # @param output_path [String] Path for repaired archive
20
20
  def run(input_path, output_path)
21
- require "omnizip/formats/rar"
22
-
23
21
  raise "Archive not found: #{input_path}" unless File.exist?(input_path)
24
22
 
25
23
  # Detect format