Class: Cabriolet::CAB::Decompressor
- Inherits:
-
Object
- Object
- Cabriolet::CAB::Decompressor
- Defined in:
- lib/cabriolet/cab/decompressor.rb
Overview
Decompressor is the main interface for CAB file operations
Instance Attribute Summary collapse
-
#buffer_size ⇒ Object
Returns the value of attribute buffer_size.
-
#fix_mszip ⇒ Object
Returns the value of attribute fix_mszip.
-
#io_system ⇒ Object
readonly
Returns the value of attribute io_system.
-
#parser ⇒ Object
readonly
Returns the value of attribute parser.
-
#salvage ⇒ Object
Returns the value of attribute salvage.
-
#search_buffer_size ⇒ Object
Returns the value of attribute search_buffer_size.
Instance Method Summary collapse
-
#append(cabinet, next_cabinet) ⇒ Boolean
Append a cabinet to another, merging their folders and files.
-
#create_decompressor(folder, input, output) ⇒ Decompressors::Base
Create appropriate decompressor for a folder.
-
#extract_all(cabinet, output_dir, **options) ⇒ Integer
Extract all files from the cabinet.
-
#extract_file(file, output_path, **options) ⇒ Integer
Extract a single file from the cabinet.
-
#initialize(io_system = nil, algorithm_factory = nil) ⇒ Decompressor
constructor
Initialize a new CAB decompressor.
-
#open(filename) ⇒ Models::Cabinet
Open and parse a CAB file.
-
#prepend(cabinet, prev_cabinet) ⇒ Boolean
Prepend a cabinet to another, merging their folders and files.
-
#search(filename) ⇒ Models::Cabinet?
Search for embedded CAB files within a file.
Constructor Details
#initialize(io_system = nil, algorithm_factory = nil) ⇒ Decompressor
Initialize a new CAB decompressor
14 15 16 17 18 19 20 21 22 |
# File 'lib/cabriolet/cab/decompressor.rb', line 14 def initialize(io_system = nil, algorithm_factory = nil) @io_system = io_system || System::IOSystem.new @algorithm_factory = algorithm_factory || Cabriolet.algorithm_factory @parser = Parser.new(@io_system) @buffer_size = Cabriolet.default_buffer_size @fix_mszip = false @salvage = false @search_buffer_size = 32_768 end |
Instance Attribute Details
#buffer_size ⇒ Object
Returns the value of attribute buffer_size.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def buffer_size @buffer_size end |
#fix_mszip ⇒ Object
Returns the value of attribute fix_mszip.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def fix_mszip @fix_mszip end |
#io_system ⇒ Object (readonly)
Returns the value of attribute io_system.
7 8 9 |
# File 'lib/cabriolet/cab/decompressor.rb', line 7 def io_system @io_system end |
#parser ⇒ Object (readonly)
Returns the value of attribute parser.
7 8 9 |
# File 'lib/cabriolet/cab/decompressor.rb', line 7 def parser @parser end |
#salvage ⇒ Object
Returns the value of attribute salvage.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def salvage @salvage end |
#search_buffer_size ⇒ Object
Returns the value of attribute search_buffer_size.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def search_buffer_size @search_buffer_size end |
Instance Method Details
#append(cabinet, next_cabinet) ⇒ Boolean
Append a cabinet to another, merging their folders and files
81 82 83 |
# File 'lib/cabriolet/cab/decompressor.rb', line 81 def append(cabinet, next_cabinet) merge_cabinets(cabinet, next_cabinet) end |
#create_decompressor(folder, input, output) ⇒ Decompressors::Base
Create appropriate decompressor for a folder
61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/cabriolet/cab/decompressor.rb', line 61 def create_decompressor(folder, input, output) @algorithm_factory.create( folder.compression_method, :decompressor, @io_system, input, output, @buffer_size, fix_mszip: @fix_mszip, salvage: @salvage, window_bits: folder.compression_level, ) end |
#extract_all(cabinet, output_dir, **options) ⇒ Integer
Extract all files from the cabinet
50 51 52 53 |
# File 'lib/cabriolet/cab/decompressor.rb', line 50 def extract_all(cabinet, output_dir, **) extractor = Extractor.new(@io_system, self) extractor.extract_all(cabinet, output_dir, **) end |
#extract_file(file, output_path, **options) ⇒ Integer
Extract a single file from the cabinet
39 40 41 42 |
# File 'lib/cabriolet/cab/decompressor.rb', line 39 def extract_file(file, output_path, **) extractor = Extractor.new(@io_system, self) extractor.extract_file(file, output_path, **) end |
#open(filename) ⇒ Models::Cabinet
Open and parse a CAB file
29 30 31 |
# File 'lib/cabriolet/cab/decompressor.rb', line 29 def open(filename) @parser.parse(filename) end |
#prepend(cabinet, prev_cabinet) ⇒ Boolean
Prepend a cabinet to another, merging their folders and files
91 92 93 |
# File 'lib/cabriolet/cab/decompressor.rb', line 91 def prepend(cabinet, prev_cabinet) merge_cabinets(prev_cabinet, cabinet) end |
#search(filename) ⇒ Models::Cabinet?
Search for embedded CAB files within a file
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/cabriolet/cab/decompressor.rb', line 99 def search(filename) # Reuse search buffer across searches for better performance search_buf = @search_buffer ||= Array.new(@search_buffer_size) first_cabinet = nil link_cabinet = nil first_len = 0 false_cabs = 0 handle = @io_system.open(filename, Constants::MODE_READ) file_length = handle.size # Check for InstallShield header at start of file if file_length >= 4 header = @io_system.read(handle, 4) @io_system.seek(handle, 0, Constants::SEEK_START) if header.unpack1("V") == 0x28635349 @io_system.(handle, "WARNING; found InstallShield header. Use unshield " \ "(https://github.com/twogood/unshield) to unpack this file") end end offset = 0 while offset < file_length # Calculate read length length = [file_length - offset, @search_buffer_size].min # Read chunk @io_system.seek(handle, offset, Constants::SEEK_START) bytes_read = @io_system.read(handle, length) break if bytes_read.nil? || bytes_read.empty? search_buf[0, bytes_read.bytesize] = bytes_read.bytes # Search for cabinets in this chunk cab_offset = find_cabinet_in_buffer(search_buf, bytes_read.size, offset, handle, filename, file_length) if cab_offset # Try to parse cabinet at this offset cabinet = try_parse_cab_at_offset(handle, filename, cab_offset) if cabinet # Capture first cabinet length first_len = cabinet.length if cab_offset.zero? # Link into list if first_cabinet.nil? first_cabinet = cabinet else link_cabinet.next = cabinet end link_cabinet = cabinet # Continue searching after this cabinet offset = cab_offset + cabinet.length else false_cabs += 1 # Restart search after signature offset = cab_offset + 4 end else # No cabinet found in this chunk, move to next offset += length end end @io_system.close(handle) # Warn about truncated/extra data if first_len.positive? && first_len != file_length && (first_cabinet.nil? || first_cabinet.base_offset.zero?) if first_len < file_length @io_system.(handle, "WARNING; possible #{file_length - first_len} extra bytes at end of file.") else @io_system.(handle, "WARNING; file possibly truncated by #{first_len - file_length} bytes.") end end if false_cabs.positive? && Cabriolet.verbose @io_system.(handle, "#{false_cabs} false cabinets found") end first_cabinet rescue StandardError @io_system.close(handle) if handle raise end |