Module: Archive::Tar::Minitar

Defined in:
lib/folio/minitar.rb

Overview

Archive::Tar::Minitar 0.5.1

Archive::Tar::Minitar is a pure-Ruby library and command-line utility that provides the ability to deal with POSIX tar(1) archive files. The implementation is based heavily on Mauricio Fern�dez’s implementation in rpa-base, but has been reorganised to promote reuse in other projects.

This tar class performs a subset of all tar (POSIX tape archive) operations. We can only deal with typeflags 0, 1, 2, and 5 (see Archive::Tar::PosixHeader). All other typeflags will be treated as normal files.

NOTE:

support for typeflags 1 and 2 is not yet implemented in this version.

This release is version 0.5.1. The library can only handle files and directories at this point. A future version will be expanded to handle symbolic links and hard links in a portable manner. The command line utility, minitar, can only create archives, extract from archives, and list archive contents.

Synopsis

Using this library is easy. The simplest case is:

require 'zlib'
require 'archive/tar/minitar'
include Archive::Tar

  # Packs everything that matches Find.find('tests')
File.open('test.tar', 'wb') { |tar| Minitar.pack('tests', tar) }
  # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
Minitar.unpack('test.tar', 'x')

A gzipped tar can be written with:

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

tgz = Zlib::GzipReader.new(File.open('test.tgz', 'rb'))
  # Warning: tgz will be closed!
Minitar.unpack(tgz, '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

Copyright 2004 Mauricio Julio Fern�dez Pradier and Austin Ziegler

This program is based on and incorporates parts of RPA::Package from rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been adapted to be more generic by Austin.

‘minitar’ contains an adaptation of Ruby/ProgressBar by Satoru Takabayashi <[email protected]>, copyright 2001 - 2004.

This program is free software. It may be redistributed and/or modified under the terms of the GPL version 2 (or later) or Ruby’s licence.

Defined Under Namespace

Classes: BlockRequired, ClosedStream, FileNameTooLong, Input, NonSeekableStream, Output, Reader, UnexpectedEOF, Writer

Constant Summary collapse

VERSION =
"0.5.1"

Class Method Summary collapse

Class Method Details

.dir?(path) ⇒ Boolean

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

Returns:

  • (Boolean)


914
915
916
# File 'lib/folio/minitar.rb', line 914

def dir?(path)
  File.directory?((path[-1] == ?/) ? path : "#{path}/")
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.



921
922
923
924
925
926
927
928
929
930
# File 'lib/folio/minitar.rb', line 921

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 argument to Find.find; all files matching will be packed.



1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
# File 'lib/folio/minitar.rb', line 1023

def pack(src, dest, recurse_dirs = true, &block)
  Output.open(dest) do |outp|
    if src.kind_of?(Array)
      src.each do |entry|
        pack_file(entry, outp, &block)
        if dir?(entry) and recurse_dirs
          Dir["#{entry}/**/**"].each do |ee|
            pack_file(ee, outp, &block)
          end
        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 packs 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 tarchive. 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.



967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
# File 'lib/folio/minitar.rb', line 967

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

  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 RUBY_PLATFORM =~ /win32/
    stats[:uid]  = nil
    stats[:gid]  = nil
  else
    stats[:uid]  ||= stat.uid
    stats[:gid]  ||= stat.gid
  end

  case
  when 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
  when dir?(name)
    yield :dir, name, stats if block_given?
    outputter.mkdir(name, stats)
  else
    raise "Don't yet know how to pack this type of file."
  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.



1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
# File 'lib/folio/minitar.rb', line 1045

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

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