ruby-xz 0.2.1 → 0.2.2

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.
@@ -1,9 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
+ #--
2
3
  # The MIT License
3
4
  #
4
5
  # Basic liblzma-bindings for Ruby.
5
6
  #
6
- # Copyright © 2011,2013 Marvin Gülker et al.
7
+ # Copyright © 2011,2013,2015 Marvin Gülker et al.
7
8
  #
8
9
  # Permission is hereby granted, free of charge, to any person obtaining a
9
10
  # copy of this software and associated documentation files (the ‘Software’),
@@ -22,19 +23,20 @@
22
23
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
24
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
25
  # THE SOFTWARE.
26
+ #++
25
27
 
26
28
  module XZ
27
29
 
28
- #This module wraps functions and enums used by liblzma.
30
+ # This module wraps functions and enums used by liblzma.
29
31
  module LibLZMA
30
32
  extend FFI::Library
31
33
 
32
- #The maximum value of an uint64_t, as defined by liblzma.
33
- #Should be the same as
34
- # (2 ** 64) - 1
34
+ # The maximum value of an uint64_t, as defined by liblzma.
35
+ # Should be the same as
36
+ # (2 ** 64) - 1
35
37
  UINT64_MAX = 18446744073709551615
36
38
 
37
- #Activates extreme compression. Same as xz's "-e" commandline switch.
39
+ # Activates extreme compression. Same as xz's "-e" commandline switch.
38
40
  LZMA_PRESET_EXTREME = 1 << 31
39
41
 
40
42
  LZMA_TELL_NO_CHECK = 0x02
@@ -42,22 +44,30 @@ module XZ
42
44
  LZMA_TELL_ANY_CHECK = 0x04
43
45
  LZMA_CONCATENATED = 0x08
44
46
 
45
- #Placeholder enum used by liblzma for later additions.
47
+ # For access convenience of the above flags.
48
+ LZMA_DECODE_FLAGS = {
49
+ :tell_no_check => LZMA_TELL_NO_CHECK,
50
+ :tell_unsupported_check => LZMA_TELL_UNSUPPORTED_CHECK,
51
+ :tell_any_check => LZMA_TELL_ANY_CHECK,
52
+ :concatenated => LZMA_CONCATENATED
53
+ }.freeze
54
+
55
+ # Placeholder enum used by liblzma for later additions.
46
56
  LZMA_RESERVED_ENUM = enum :lzma_reserved_enum, 0
47
57
 
48
- #Actions that can be passed to the lzma_code() function.
58
+ # Actions that can be passed to the lzma_code() function.
49
59
  LZMA_ACTION = enum :lzma_run, 0,
50
60
  :lzma_sync_flush,
51
61
  :lzma_full_flush,
52
62
  :lzma_finish
53
63
 
54
- #Integrity check algorithms supported by liblzma.
64
+ # Integrity check algorithms supported by liblzma.
55
65
  LZMA_CHECK = enum :lzma_check_none, 0,
56
66
  :lzma_check_crc32, 1,
57
67
  :lzma_check_crc64, 4,
58
68
  :lzma_check_sha256, 10
59
69
 
60
- #Possible return values of liblzma functions.
70
+ # Possible return values of liblzma functions.
61
71
  LZMA_RET = enum :lzma_ok, 0,
62
72
  :lzma_stream_end,
63
73
  :lzma_no_check,
@@ -80,10 +90,10 @@ module XZ
80
90
 
81
91
  end
82
92
 
83
- #The class of the error that this library raises.
93
+ # The class of the error that this library raises.
84
94
  class LZMAError < StandardError
85
95
 
86
- #Raises an appropriate exception if +val+ isn't a liblzma success code.
96
+ # Raises an appropriate exception if +val+ isn't a liblzma success code.
87
97
  def self.raise_if_necessary(val)
88
98
  case LibLZMA::LZMA_RET[val]
89
99
  when :lzma_mem_error then raise(self, "Couldn't allocate memory!")
@@ -98,7 +108,7 @@ module XZ
98
108
 
99
109
  end
100
110
 
101
- #The main struct of the liblzma library.
111
+ # The main struct of the liblzma library.
102
112
  class LZMAStream < FFI::Struct
103
113
  layout :next_in, :pointer, #uint8
104
114
  :avail_in, :size_t,
@@ -119,11 +129,11 @@ module XZ
119
129
  :reserved_enum1, :int,
120
130
  :reserved_enum2, :int
121
131
 
122
- #This method does basicly the same thing as the
123
- #LZMA_STREAM_INIT macro of liblzma. Creates a new LZMAStream
124
- #that has been initialized for usage. If any argument is passed,
125
- #it is assumed to be a FFI::Pointer to a lzma_stream structure
126
- #and that structure is wrapped.
132
+ # This method does basicly the same thing as the
133
+ # LZMA_STREAM_INIT macro of liblzma. Creates a new LZMAStream
134
+ # that has been initialized for usage. If any argument is passed,
135
+ # it is assumed to be a FFI::Pointer to a lzma_stream structure
136
+ # and that structure is wrapped.
127
137
  def initialize(*args)
128
138
  if !args.empty? #Got a pointer, want to wrap it
129
139
  super
@@ -1,9 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
+ #--
2
3
  # (The MIT license)
3
4
  #
4
5
  # Basic liblzma-bindings for Ruby.
5
6
  #
6
- # Copyright © 2012 Marvin Gülker
7
+ # Copyright © 2012, 2015 Marvin Gülker
7
8
  #
8
9
  # Permission is hereby granted, free of charge, to any person obtaining a
9
10
  # copy of this software and associated documentation files (the ‘Software’),
@@ -22,30 +23,31 @@
22
23
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
24
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
25
  # THE SOFTWARE.
26
+ #++
25
27
 
26
- #The base class for XZ::StreamReader and XZ::StreamWriter.
27
- #This is an abstract class that is not meant to be used
28
- #directly; if you try, you will soon recognise that you’ve
29
- #created a quite limited object ;-). You can, however, test
30
- #against this class in <tt>kind_of?</tt> tests.
28
+ # The base class for XZ::StreamReader and XZ::StreamWriter.
29
+ # This is an abstract class that is not meant to be used
30
+ # directly; if you try, you will soon recognise that you’ve
31
+ # created a quite limited object ;-). You can, however, test
32
+ # against this class in <tt>kind_of?</tt> tests.
31
33
  #
32
- #XZ::StreamReader and XZ::StreamWriter are IO-like classes that
33
- #allow you to access XZ-compressed data the same way you access
34
- #an IO-object, easily allowing to fool other libraries that expect
35
- #IO objects. The most noticable example for this may be reading
36
- #and writing XZ-compressed tarballs; see XZ::StreamReader and
37
- #XZ::StreamWriter for respective examples.
34
+ # XZ::StreamReader and XZ::StreamWriter are IO-like classes that
35
+ # allow you to access XZ-compressed data the same way you access
36
+ # an IO-object, easily allowing to fool other libraries that expect
37
+ # IO objects. The most noticable example for this may be reading
38
+ # and writing XZ-compressed tarballs; see XZ::StreamReader and
39
+ # XZ::StreamWriter for respective examples.
38
40
  #
39
- #Neither this class nor its subclasses document the IO-methods
40
- #they contain--this is due to the reason that they include the
41
- #great IO::Like module that provides all the necessary IO methods
42
- #based on a few methods you define. For all defined IO methods,
43
- #see the +io-like+ gem’s documentation.
41
+ # Neither this class nor its subclasses document the IO-methods
42
+ # they contain--this is due to the reason that they include the
43
+ # great IO::Like module that provides all the necessary IO methods
44
+ # based on a few methods you define. For all defined IO methods,
45
+ # see the +io-like+ gem’s documentation.
44
46
  class XZ::Stream
45
47
  include IO::Like
46
48
 
47
- #Creates a new instance of this class. Don’t use this directly,
48
- #it’s only called by subclasses’ ::new methods.
49
+ # Creates a new instance of this class. Don’t use this directly,
50
+ # it’s only called by subclasses’ ::new methods.
49
51
  def initialize(delegate_io)
50
52
  @delegate_io = delegate_io
51
53
  @lzma_stream = XZ::LZMAStream.new
@@ -53,10 +55,10 @@ class XZ::Stream
53
55
 
54
56
  private
55
57
 
56
- #This method returns the size of +str+ in bytes.
58
+ # This method returns the size of +str+ in bytes.
57
59
  def binary_size(str)
58
- #Believe it or not, but this is faster than str.bytes.to_a.size.
59
- #I benchmarked it, and it is as twice as fast.
60
+ # Believe it or not, but this is faster than str.bytes.to_a.size.
61
+ # I benchmarked it, and it is as twice as fast.
60
62
  if str.respond_to? :force_encoding
61
63
  str.dup.force_encoding(Encoding::BINARY).size
62
64
  else
@@ -1,9 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
+ #--
2
3
  # (The MIT license)
3
4
  #
4
5
  # Basic liblzma-bindings for Ruby.
5
6
  #
6
- # Copyright © 2012 Marvin Gülker
7
+ # Copyright © 2012, 2015 Marvin Gülker
7
8
  #
8
9
  # Permission is hereby granted, free of charge, to any person obtaining a
9
10
  # copy of this software and associated documentation files (the ‘Software’),
@@ -22,97 +23,166 @@
22
23
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
24
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
25
  # THE SOFTWARE.
26
+ #++
25
27
 
26
- #An IO-like reader class for XZ-compressed data, allowing you to
27
- #access XZ-compressed data as if it was a normal IO object, but
28
- #please note you can’t seek in the data--this doesn’t make much
29
- #sense anyway. Where would you want to seek? The plain or the XZ
30
- #data?
28
+ # An IO-like reader class for XZ-compressed data, allowing you to
29
+ # access XZ-compressed data as if it was a normal IO object, but
30
+ # please note you can’t seek in the data--this doesn’t make much
31
+ # sense anyway. Where would you want to seek? The plain or the XZ
32
+ # data?
31
33
  #
32
- #A StreamReader object actually wraps another IO object it reads
33
- #the compressed data from; you can either pass this IO object directly
34
- #to the ::new method, effectively allowing you to pass any IO-like thing
35
- #you can imagine (just ensure it is readable), or you can pass a path
36
- #to a filename to ::new, in which case StreamReader takes care of both
37
- #opening and closing the file correctly. You can even take it one step
38
- #further and use the block form of ::new which will automatically call
39
- #the #close method for you after the block finished. However, if you pass
40
- #an IO, remember you have to close:
34
+ # A StreamReader object actually wraps another IO object it reads
35
+ # the compressed data from; you can either pass this IO object directly
36
+ # to the ::new method, effectively allowing you to pass any IO-like thing
37
+ # you can imagine (just ensure it is readable), or you can pass a path
38
+ # to a filename to ::open, in which case StreamReader takes care of both
39
+ # opening and closing the file correctly. You can even take it one step
40
+ # further and use the block form of ::new and ::open, which will automatically call
41
+ # the #close method for you after the block finished. However, if you pass
42
+ # an IO, remember you have to close:
41
43
  #
42
- #1. The StreamReader instance.
43
- #2. The IO object you passed to ::new.
44
+ # 1. The StreamReader instance.
45
+ # 2. The IO object you passed to ::new.
44
46
  #
45
- #Do it <b>in exactly that order</b>, otherwise you may lose data.
47
+ # Do it <b>in exactly that order</b>, otherwise you may lose data.
46
48
  #
47
- #See the +io-like+ gem’s documentation for the IO-reading methods
48
- #available for this class (although you’re probably familiar with
49
- #them through Ruby’s own IO class ;-)).
49
+ # *WARNING*: The closing behaviour described above is subject to
50
+ # change in the next major version. In the future, wrapped IO
51
+ # objects are automatically closed always, regardless of whether you
52
+ # passed a filename or an IO instance. This is to sync the API with
53
+ # Ruby’s own Zlib::GzipReader. To prevent that, call #finish instead
54
+ # of #close.
50
55
  #
51
- #==Example
52
- #In this example, we’re going to use ruby-xz together with the
53
- #+archive-tar-minitar+ gem that allows to read tarballs. Used
54
- #together, the two libraries allow us to read XZ-compressed tarballs.
56
+ # See the +io-like+ gem’s documentation for the IO-reading methods
57
+ # available for this class (although you’re probably familiar with
58
+ # them through Ruby’s own IO class ;-)).
55
59
  #
56
- # require "xz"
57
- # require "archive/tar/minitar"
60
+ # ==Example
61
+ # In this example, we’re going to use ruby-xz together with the
62
+ # +archive-tar-minitar+ gem that allows to read tarballs. Used
63
+ # together, the two libraries allow us to read XZ-compressed tarballs.
58
64
  #
59
- # XZ::StreamReader.open("foo.tar.xz") do |txz|
60
- # # This automatically closes txz
61
- # Archive::Tar::Minitar.unpack(txz, "foo")
62
- # end
65
+ # require "xz"
66
+ # require "archive/tar/minitar"
67
+ #
68
+ # XZ::StreamReader.open("foo.tar.xz") do |txz|
69
+ # # This automatically closes txz
70
+ # Archive::Tar::Minitar.unpack(txz, "foo")
71
+ # end
63
72
  class XZ::StreamReader < XZ::Stream
64
73
 
65
- #The memory limit you set for this reader (in ::new).
74
+ # The memory limit you set for this reader (in ::new).
66
75
  attr_reader :memory_limit
67
- #The flags you set for this reader (in ::new).
68
- attr_reader :flags
69
76
 
70
- #call-seq:
71
- # new(delegate, memory_limit = XZ::LibLZMA::UINT64_MAX, flags = [:tell_unsupported_check]) → a_stream_reader
72
- # open(delegate, memory_limit = XZ::LibLZMA::UINT64_MAX, flags = [:tell_unsupported_check]) → a_stream_reader
73
- #
74
- #Creates a new StreamReader instance. If you pass an IO,
75
- #remember you have to close *both* the resulting instance
76
- #(via the #close method) and the IO object you pass to flush
77
- #any internal buffers in order to be able to read all decompressed
78
- #data.
79
- #==Parameters
80
- #[delegate] An IO object to read the data from, or a path
81
- # to a file to open. If you’re in an urgent need to
82
- # pass a plain string, use StringIO from Ruby’s
83
- # standard library. If this is an IO, it must be
84
- # opened for reading.
85
- #The other parameters are identical to what the XZ::decompress_stream
86
- #method expects.
87
- #==Return value
88
- #The newly created instance.
89
- #==Example
90
- # # Wrap it around a file
91
- # f = File.open("foo.xz")
92
- # r = XZ::StreamReader.new(f)
93
- #
94
- # # Ignore any XZ checksums (may result in invalid data being read!)
95
- # File.open("foo.xz") do |f|
96
- # r = XZ::StreamReader.new(f, XZ::LibLZMA::UINT64_MAX, [:tell_no_check]
97
- # end
98
- #
99
- # # Let StreamReader handle file closing automatically
100
- # XZ::StreamReader.new("myfile.xz"){|r| r.raed}
101
- def initialize(delegate, memory_limit = XZ::LibLZMA::UINT64_MAX, flags = [:tell_unsupported_check])
102
- raise(ArgumentError, "Invalid memory limit set!") unless (0..XZ::LibLZMA::UINT64_MAX).include?(memory_limit)
103
- flags.each do |flag|
104
- raise(ArgumentError, "Unknown flag #{flag}!") unless [:tell_no_check, :tell_unsupported_check, :tell_any_check, :concatenated].include?(flag)
105
- end
77
+ # The flags you set for this reader (in ::new).
78
+ attr_reader :flags
106
79
 
80
+ # call-seq:
81
+ # new(delegate, opts = {}) → reader
82
+ # new(delegate, opts = {}){|reader| …} → obj
83
+ #
84
+ # Creates a new StreamReader instance. If you pass an IO,
85
+ # remember you have to close *both* the resulting instance
86
+ # (via the #close method) and the IO object you pass to flush
87
+ # any internal buffers in order to be able to read all decompressed
88
+ # data (beware Deprecations section below).
89
+ #
90
+ # === Parameters
91
+ #
92
+ # [delegate]
93
+ # An IO object to read the data from, If you’re in an urgent
94
+ # need to pass a plain string, use StringIO from Ruby’s
95
+ # standard library. If this is an IO, it must be
96
+ # opened for reading.
97
+ #
98
+ # [opts]
99
+ # Options hash accepting these parameters (defaults indicated
100
+ # in parantheses):
101
+ #
102
+ # [:memory_limit (LibLZMA::UINT64_MAX)]
103
+ # If not XZ::LibLZMA::UINT64_MAX, makes liblzma use
104
+ # no more memory than this amount of bytes.
105
+ #
106
+ # [:flags ([:tell_unsupported_check])]
107
+ # Additional flags passed to libzlma (an array). Possible
108
+ # flags are:
109
+ #
110
+ # [:tell_no_check]
111
+ # Spit out a warning if the archive hasn’t an integrity
112
+ # checksum.
113
+ # [:tell_unsupported_check]
114
+ # Spit out a warning if the archive has an unsupported
115
+ # checksum type.
116
+ # [:concatenated]
117
+ # Decompress concatenated archives.
118
+ #
119
+ # [reader]
120
+ # Block argument. self of the new instance.
121
+ #
122
+ # === Return value
123
+ #
124
+ # The block form returns the block’s last expression, the nonblock
125
+ # form returns the newly created instance.
126
+ #
127
+ # === Deprecations
128
+ #
129
+ # The old API for this method as it was documented in version 0.2.1
130
+ # still works, but is deprecated. Please change to the new API as
131
+ # soon as possible.
132
+ #
133
+ # *WARNING*: The closing behaviour of the block form is subject to
134
+ # upcoming change. In the next major release the wrapped IO *will*
135
+ # be automatically closed, unless you call #finish.
136
+ #
137
+ # === Example
138
+ #
139
+ # # Wrap it around a file
140
+ # f = File.open("foo.xz")
141
+ # r = XZ::StreamReader.new(f)
142
+ #
143
+ # # Ignore any XZ checksums (may result in invalid
144
+ # # data being read!)
145
+ # File.open("foo.xz") do |f|
146
+ # r = XZ::StreamReader.new(f, :flags => [:tell_no_check])
147
+ # end
148
+ def initialize(delegate, *args)
107
149
  if delegate.respond_to?(:to_io)
108
- super(delegate)
150
+ # Correct use with IO
151
+ super(delegate.to_io)
152
+ @autoclose = false
109
153
  else
110
- @file = File.open(delegate, "rb")
111
- super(@file)
154
+ # Deprecated use of filename
155
+ XZ.deprecate "Calling XZ::StreamReader.new with a filename is deprecated, use XZ::StreamReader.open instead."
156
+
157
+ @autoclose = true
158
+ super(File.open(delegate, "rb"))
159
+ end
160
+
161
+ # Flag for calling #finish
162
+ @finish = false
163
+
164
+ opts = {}
165
+ if args[0].kind_of?(Hash) # New API
166
+ opts = args[0]
167
+ opts[:memory_limit] ||= XZ::LibLZMA::UINT64_MAX
168
+ opts[:flags] ||= [:tell_unsupported_check]
169
+ else # Old API
170
+ # no arguments may also happen in new API
171
+ unless args.empty?
172
+ XZ.deprecate "Calling XZ::StreamReader.new with explicit arguments is deprecated, use an options hash instead."
173
+ end
174
+
175
+ opts[:memory_limit] = args[0] || XZ::LibLZMA::UINT64_MAX
176
+ opts[:flags] = args[1] || [:tell_unsupported_check]
177
+ end
178
+
179
+ raise(ArgumentError, "Invalid memory limit set!") unless (0..XZ::LibLZMA::UINT64_MAX).include?(opts[:memory_limit])
180
+ opts[:flags].each do |flag|
181
+ raise(ArgumentError, "Unknown flag #{flag}!") unless [:tell_no_check, :tell_unsupported_check, :tell_any_check, :concatenated].include?(flag)
112
182
  end
113
183
 
114
- @memory_limit = memory_limit
115
- @flags = flags
184
+ @memory_limit = opts[:memory_limit]
185
+ @flags = opts[:flags]
116
186
 
117
187
  res = XZ::LibLZMA.lzma_stream_decoder(@lzma_stream,
118
188
  @memory_limit,
@@ -133,17 +203,98 @@ class XZ::StreamReader < XZ::Stream
133
203
  end
134
204
  end
135
205
  end
136
- self.class.send(:alias_method, :open, :new)
137
-
138
- #Closes this StreamReader instance. Don’t use it afterwards
139
- #anymore.
140
- #==Return value
141
- #The total number of bytes decompressed.
142
- #==Example
143
- # r.close #=> 6468
144
- #==Remarks
145
- #If you passed an IO to ::new, this method doesn’t close it, so
146
- #you have to close it yourself.
206
+
207
+ # call-seq:
208
+ # open(filename, opts = {}) reader
209
+ # open(filename, opts = {}){|reader| …} → obj
210
+ #
211
+ # Opens a file from disk and wraps an XZ::StreamReader instance
212
+ # around the resulting File IO object. This is a convenience
213
+ # method that is equivalent to calling
214
+ #
215
+ # file = File.open(filename, "rb")
216
+ # reader = XZ::StreamReader.new(file, opts)
217
+ #
218
+ # , except that you don’t have to explicitely close the File
219
+ # instance, this is done automatically when you call #close.
220
+ # Beware the Deprecations section in this regard.
221
+ #
222
+ # === Parameters
223
+ #
224
+ # [filename]
225
+ # Path to a file on the disk to open. This file should
226
+ # exist and be readable, otherwise you may get Errno
227
+ # exceptions.
228
+ #
229
+ # [opts]
230
+ # Options hash. See ::new for a description of the possible
231
+ # options.
232
+ #
233
+ # [reader]
234
+ # Block argument. self of the new instance.
235
+ #
236
+ # === Return value
237
+ #
238
+ # The block form returns the block’s last expression, the nonblock
239
+ # form returns the newly created XZ::StreamReader instance.
240
+ #
241
+ # === Deprecations
242
+ #
243
+ # In the API up to and including version 0.2.1 this method was an
244
+ # alias for ::new. This continues to work for now, but using it
245
+ # as an alias for ::new is deprecated. The next major version will
246
+ # only accept a string as a parameter for this method.
247
+ #
248
+ # *WARNING*: Future versions of ruby-xz will always close the
249
+ # wrapped IO, regardless of whether you pass in your own IO or use
250
+ # this convenience method! To prevent that, call the #finish method.
251
+ #
252
+ # === Examples
253
+ #
254
+ # XZ::StreamReader.new("myfile.xz"){|r| r.read}
255
+ def self.open(filename, *args, &block)
256
+ if filename.respond_to?(:to_io)
257
+ # Deprecated use of IO
258
+ XZ.deprecate "Calling XZ::StreamReader.open with an IO is deprecated, use XZ::StreamReader.new instead"
259
+ new(filename.to_io, *args, &block)
260
+ else
261
+ # Correct use with filename
262
+ file = File.open(filename, "rb")
263
+
264
+ obj = new(file, *args)
265
+ obj.instance_variable_set(:@autoclose, true) # Only needed during deprecation phase (see #close)
266
+
267
+ if block_given?
268
+ begin
269
+ block.call(obj)
270
+ ensure
271
+ obj.close unless obj.closed?
272
+ end
273
+ else
274
+ obj
275
+ end
276
+ end
277
+ end
278
+
279
+ # Closes this StreamReader instance. Don’t use it afterwards
280
+ # anymore.
281
+ #
282
+ # === Return value
283
+ #
284
+ # The total number of bytes decompressed.
285
+ #
286
+ # === Example
287
+ #
288
+ # r.close #=> 6468
289
+ #
290
+ # === Remarks
291
+ #
292
+ # If you passed an IO to ::new, this method doesn’t close it, so
293
+ # you have to close it yourself.
294
+ #
295
+ # *WARNING*: The next major release will change this behaviour.
296
+ # In the future, the wrapped IO object will always be closed.
297
+ # Use the #finish method for keeping it open.
147
298
  def close
148
299
  super
149
300
 
@@ -151,43 +302,98 @@ class XZ::StreamReader < XZ::Stream
151
302
  res = XZ::LibLZMA.lzma_end(@lzma_stream.pointer)
152
303
  XZ::LZMAError.raise_if_necessary(res)
153
304
 
154
- #If we created a File object, close this as well.
155
- @file.close if @file
305
+ unless @finish
306
+ # New API: Close the wrapped IO
307
+ #@delegate_io.close
308
+ # ↑ uncomment on API break and remove OLD API below. Note that with
309
+ # the new API that always closes the underlying IO, it is not necessary
310
+ # to distinguish a self-opened IO from a wrapped preexisting IO.
311
+ # The variable @autoclose can thus be removed on API break.
312
+
313
+ # Old API:
314
+ #If we created a File object, close this as well.
315
+ if @autoclose
316
+ # This does not change in the new API, so no deprecation warning.
317
+ @delegate_io.close
318
+ else
319
+ XZ.deprecate "XZ::StreamReader#close will automatically close the wrapped IO in the future. Use #finish to prevent that."
320
+ end
321
+ end
156
322
 
157
323
  # Return the number of bytes written in total.
158
324
  @lzma_stream[:total_out]
159
325
  end
160
326
 
161
- #call-seq:
162
- # pos() → an_integer
163
- # tell() an_integer
327
+ # If called in the block form of ::new or ::open, prevents the
328
+ # wrapped IO from being closed, only the LZMA stream is closed
329
+ # then. If called outside the block form of ::new and open, behaves
330
+ # like #close, but only closes the underlying LZMA stream. The
331
+ # wrapped IO object is kept open.
332
+ #
333
+ # === Return value
334
+ #
335
+ # Returns the wrapped IO object. This allows you to wire the File
336
+ # instance out of a StreamReader instance that was created with
337
+ # ::open.
338
+ #
339
+ # === Example
164
340
  #
165
- #Total number of output bytes provided to you yet.
341
+ # # Nonblock form
342
+ # f = File.open("foo.xz", "rb")
343
+ # r = XZ::StreamReader.new(f)
344
+ # r.finish
345
+ # # f is still open here!
346
+ #
347
+ # # Block form
348
+ # str = nil
349
+ # f = XZ::StreamReader.open("foo.xz") do |r|
350
+ # str = r.read
351
+ # r.finish
352
+ # end
353
+ # # f now is an *open* File instance of mode "rb".
354
+ def finish
355
+ # Do not close wrapped IO object in #close
356
+ @finish = true
357
+ close
358
+
359
+ @delegate_io
360
+ end
361
+
362
+ # call-seq:
363
+ # pos() → an_integer
364
+ # tell() → an_integer
365
+ #
366
+ # Total number of output bytes provided to you yet.
166
367
  def pos
167
368
  @lzma_stream[:total_out]
168
369
  end
169
370
  alias tell pos
170
371
 
171
- #Instrcuts liblzma to immediately stop decompression,
172
- #rewinds the wrapped IO object and reinitalizes the
173
- #StreamReader instance with the same values passed
174
- #originally to the ::new method. The wrapped IO object
175
- #must support the +rewind+ method for this method to
176
- #work; if it doesn’t, this method throws an IOError.
177
- #After the exception was thrown, the StreamReader instance
178
- #is in an unusable state. You cannot continue using it
179
- #(don’t call #close on it either); close the wrapped IO
180
- #stream and create another instance of this class.
181
- #==Raises
182
- #[IOError] The wrapped IO doesn’t support rewinding.
183
- # Do not use the StreamReader instance anymore
184
- # after receiving this exception.
185
- #==Remarks
186
- #I don’t really like this method, it uses several dirty
187
- #tricks to circumvent both io-like’s and liblzma’s control
188
- #mechanisms. I only implemented this because the
189
- #<tt>archive-tar-minitar</tt> gem calls this method when
190
- #unpacking a TAR archive from a stream.
372
+ # Instrcuts liblzma to immediately stop decompression,
373
+ # rewinds the wrapped IO object and reinitalizes the
374
+ # StreamReader instance with the same values passed
375
+ # originally to the ::new method. The wrapped IO object
376
+ # must support the +rewind+ method for this method to
377
+ # work; if it doesn’t, this method throws an IOError.
378
+ # After the exception was thrown, the StreamReader instance
379
+ # is in an unusable state. You cannot continue using it
380
+ # (don’t call #close on it either); close the wrapped IO
381
+ # stream and create another instance of this class.
382
+ #
383
+ # === Raises
384
+ #
385
+ # [IOError]
386
+ # The wrapped IO doesn’t support rewinding.
387
+ # Do not use the StreamReader instance anymore
388
+ # after receiving this exception.
389
+ #
390
+ # ==Remarks
391
+ #
392
+ # I don’t really like this method, it uses several dirty
393
+ # tricks to circumvent both io-like’s and liblzma’s control
394
+ # mechanisms. I only implemented this because the
395
+ # <tt>archive-tar-minitar</tt> gem calls this method when
396
+ # unpacking a TAR archive from a stream.
191
397
  def rewind
192
398
  # HACK: Wipe all data from io-like’s internal read buffer.
193
399
  # This heavily relies on io-like’s internal structure.
@@ -206,24 +412,24 @@ class XZ::StreamReader < XZ::Stream
206
412
  raise(IOError, "Delegate IO failed to rewind! Original message: #{e.message}")
207
413
  end
208
414
 
209
- # Reinitialize everything. Note this doesn’t affect @file as it
415
+ # Reinitialize everything. Note this doesn’t affect @autofile as it
210
416
  # is already set and stays so (we don’t pass a filename here,
211
417
  # but rather an IO)
212
- initialize(@delegate_io, @memory_limit, @flags)
418
+ initialize(@delegate_io, :memory_limit => @memory_limit, :flags => @flags)
213
419
  end
214
420
 
215
- #NO, you CANNOT seek in this object!!
216
- #io-like’s default behaviour is to raise Errno::ESPIPE
217
- #when calling a non-defined seek, which is not what some
218
- #libraries such as RubyGem’s TarReader expect (they expect
219
- #a NoMethodError/NameError instead).
421
+ # NO, you CANNOT seek in this object!!
422
+ # io-like’s default behaviour is to raise Errno::ESPIPE
423
+ # when calling a non-defined seek, which is not what some
424
+ # libraries such as RubyGem’s TarReader expect (they expect
425
+ # a NoMethodError/NameError instead).
220
426
  undef seek
221
427
 
222
428
  private
223
429
 
224
- #Called by io-like’s read methods such as #read. Does the heavy work
225
- #of feeding liblzma the compressed data and reading the returned
226
- #uncompressed data.
430
+ # Called by io-like’s read methods such as #read. Does the heavy work
431
+ # of feeding liblzma the compressed data and reading the returned
432
+ # uncompressed data.
227
433
  def unbuffered_read(length)
228
434
  raise(EOFError, "Input data completely processed!") if @__lzma_finished
229
435