Module: Archive::Tar::Minitar

Defined in:
lib/shoes/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?ndez'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

Copyright 2004 Mauricio Julio Fern?ndez 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 <satoru@namazu.org>, 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.

Constant Summary

Class Method Summary (collapse)

Class Method Details

+ (Boolean) dir?(path)

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



836
837
838
# File 'lib/shoes/minitar.rb', line 836

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

+ (Object) open(dest, mode = "r",, &block)

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.



843
844
845
846
847
848
849
850
851
852
# File 'lib/shoes/minitar.rb', line 843

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

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

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.



948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
# File 'lib/shoes/minitar.rb', line 948

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

+ (Object) pack_file(entry, outputter)

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.



889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
# File 'lib/shoes/minitar.rb', line 889

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 name == "sh-install" # an ugly shoes-specific hack, to
    stats[:mode] = 0755   # get the file to be 0755 on windows
  end

  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

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

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



970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
# File 'lib/shoes/minitar.rb', line 970

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