Module: Archive::Tar::Minitar

Defined in:
lib/archive/tar/minitar.rb,
lib/archive/tar/minitar/input.rb,
lib/archive/tar/minitar/output.rb,
lib/archive/tar/minitar/reader.rb,
lib/archive/tar/minitar/writer.rb,
lib/archive/tar/minitar/posix_header.rb

Overview

Synopsis

Using minitar is easy. The simplest case is:

require 'zlib'
require 'minitar'

# Packs everything that matches Find.find('tests').
# test.tar will automatically be closed by Minitar.pack.
Minitar.pack('tests', File.open('test.tar', 'wb'))

# Unpacks 'test.tar' to 'x', creating 'x' if necessary.
Minitar.unpack('test.tar', 'x')

A gzipped tar can be written with:

# test.tgz will be closed automatically.
Minitar.pack('tests', Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))

# test.tgz will be closed automatically.
Minitar.unpack(Zlib::GzipReader.new(File.open('test.tgz', 'rb')), 'x')

As the case above shows, one need not write to a file. However, it will sometimes require that one dive a little deeper into the API, as in the case of StringIO objects. Note that I’m not providing a block with Minitar::Output, as Minitar::Output#close automatically closes both the Output object and the wrapped data stream object.

begin
  sgz = Zlib::GzipWriter.new(StringIO.new(""))
  tar = Output.new(sgz)
  Find.find('tests') do |entry|
    Minitar.pack_file(entry, tar)
  end
ensure
    # Closes both tar and sgz.
  tar.close
end

Defined Under Namespace

Classes: Input, Output, PosixHeader, Reader, Writer

Constant Summary collapse

VERSION =

:nodoc:

'0.6.1'.freeze
Error =

The base class for any minitar error.

Class.new(::StandardError)
NonSeekableStream =

Raised when a wrapped data stream class is not seekable.

Class.new(Error)
ClosedStream =

The exception raised when operations are performed on a stream that has previously been closed.

Class.new(Error)
FileNameTooLong =

The exception raised when a filename exceeds 256 bytes in length, the maximum supported by the standard Tar format.

Class.new(Error)
UnexpectedEOF =

The exception raised when a data stream ends before the amount of data expected in the archive’s PosixHeader.

Class.new(StandardError)
SecureRelativePathError =

The exception raised when a file contains a relative path in secure mode (the default for this version).

Class.new(Error)

Class Method Summary collapse

Class Method Details

.const_missing(c) ⇒ Object

:nodoc:



118
119
120
121
122
123
124
125
126
# File 'lib/archive/tar/minitar.rb', line 118

def const_missing(c) #:nodoc:
  case c
  when :BlockRequired
    warn 'This constant has been removed.'
    const_set(:BlockRequired, Class.new(StandardError))
  else
    super
  end
end

.dir?(path) ⇒ Boolean

Tests if path refers to a directory. Fixes an apparently corrupted stat() call on Windows.

Returns:

  • (Boolean)


98
99
100
101
102
# File 'lib/archive/tar/minitar.rb', line 98

def dir?(path)
  # rubocop:disable Style/CharacterLiteral
  File.directory?(path[-1] == ?/ ? path : "#{path}/")
  # rubocop:enable Style/CharacterLiteral
end

.open(dest, mode = 'r', &block) ⇒ Object

A convenience method for wrapping Archive::Tar::Minitar::Input.open (mode r) and Archive::Tar::Minitar::Output.open (mode w). No other modes are currently supported.



107
108
109
110
111
112
113
114
115
116
# File 'lib/archive/tar/minitar.rb', line 107

def open(dest, mode = 'r', &block)
  case mode
  when 'r'
    Input.open(dest, &block)
  when 'w'
    Output.open(dest, &block)
  else
    raise 'Unknown open mode for Archive::Tar::Minitar.open.'
  end
end

.pack(src, dest, recurse_dirs = true, &block) ⇒ Object

A convenience method to pack files specified by src into dest. If src is an Array, then each file detailed therein will be packed into the resulting Archive::Tar::Minitar::Output stream; if recurse_dirs is true, then directories will be recursed.

If src is an Array, it will be treated as the result of Find.find; all files matching will be packed.



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/archive/tar/minitar.rb', line 222

def pack(src, dest, recurse_dirs = true, &block)
  require 'find'
  Output.open(dest) do |outp|
    if src.kind_of?(Array)
      src.each do |entry|
        if dir?(entry) and recurse_dirs
          Find.find(entry) do |ee|
            pack_file(ee, outp, &block)
          end
        else
          pack_file(entry, outp, &block)
        end
      end
    else
      Find.find(src) do |entry|
        pack_file(entry, outp, &block)
      end
    end
  end
end

.pack_file(entry, outputter) ⇒ Object

A convenience method to pack the file provided. entry may either be a filename (in which case various values for the file (see below) will be obtained from File#stat(entry) or a Hash with the fields:

:name

The filename to be packed into the archive. Required.

:mode

The mode to be applied.

:uid

The user owner of the file. (Ignored on Windows.)

:gid

The group owner of the file. (Ignored on Windows.)

:mtime

The modification Time of the file.

During packing, if a block is provided, #pack_file yields an action Symol, the full name of the file being packed, and a Hash of statistical information, just as with Archive::Tar::Minitar::Input#extract_entry.

The action will be one of:

:dir

The entry is a directory.

:file_start

The entry is a file; the extract of the file is just beginning.

:file_progress

Yielded every 4096 bytes during the extract of the entry.

:file_done

Yielded when the entry is completed.

The stats hash contains the following keys:

:current

The current total number of bytes read in the entry.

:currinc

The current number of bytes read in this read cycle.

:name

The filename to be packed into the tarchive. REQUIRED.

:mode

The mode to be applied.

:uid

The user owner of the file. (nil on Windows.)

:gid

The group owner of the file. (nil on Windows.)

:mtime

The modification Time of the file.



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/archive/tar/minitar.rb', line 166

def pack_file(entry, outputter) #:yields action, name, stats:
  if outputter.kind_of?(Archive::Tar::Minitar::Output)
    outputter = outputter.tar
  end

  stats = {}

  if entry.kind_of?(Hash)
    name = entry[:name]
    entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
  else
    name = entry
  end

  name = name.sub(%r{\./}, '')
  stat = File.stat(name)
  stats[:mode]   ||= stat.mode
  stats[:mtime]  ||= stat.mtime
  stats[:size] = stat.size

  if windows?
    stats[:uid]  = nil
    stats[:gid]  = nil
  else
    stats[:uid]  ||= stat.uid
    stats[:gid]  ||= stat.gid
  end

  if File.file?(name)
    outputter.add_file_simple(name, stats) do |os|
      stats[:current] = 0
      yield :file_start, name, stats if block_given?
      File.open(name, 'rb') do |ff|
        until ff.eof?
          stats[:currinc] = os.write(ff.read(4096))
          stats[:current] += stats[:currinc]
          yield :file_progress, name, stats if block_given?
        end
      end
      yield :file_done, name, stats if block_given?
    end
  elsif dir?(name)
    yield :dir, name, stats if block_given?
    outputter.mkdir(name, stats)
  else
    raise %q(Don't yet know how to pack this type of file.)
  end
end

.seekable?(io, methods = nil) ⇒ Boolean

Check whether io can seek without errors.

Returns:

  • (Boolean)


263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/archive/tar/minitar.rb', line 263

def seekable?(io, methods = nil)
  # The IO class throws an exception at runtime if we try to change
  # position on a non-regular file.
  if io.respond_to?(:stat)
    io.stat.file?
  else
    # Duck-type the rest of this.
    methods ||= [ :pos, :pos=, :seek, :rewind ]
    methods = [ methods ] unless methods.kind_of?(Array)
    methods.all? { |m| io.respond_to?(m) }
  end
end

.unpack(src, dest, files = [], &block) ⇒ Object

A convenience method to unpack files from src into the directory specified by dest. Only those files named explicitly in files will be extracted.



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/archive/tar/minitar.rb', line 246

def unpack(src, dest, files = [], &block)
  Input.open(src) do |inp|
    if File.exist?(dest) and !dir?(dest)
      raise %q(Can't unpack to a non-directory.)
    end

    FileUtils.mkdir_p(dest) unless File.exist?(dest)

    inp.each do |entry|
      if files.empty? or files.include?(entry.full_name)
        inp.extract_entry(dest, entry, &block)
      end
    end
  end
end

.windows?Boolean

:nodoc:

Returns:

  • (Boolean)


128
129
130
# File 'lib/archive/tar/minitar.rb', line 128

def windows? #:nodoc:
  RbConfig::CONFIG['host_os'] =~ /^(mswin|mingw|cygwin)/
end