ruby-xz 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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