Module: Archive::Tar::Minitar

Defined in:
lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb

Overview

Archive::Tar::Minitar 0.5.2

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 Ferna’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.2. 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 Ferna’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 <[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

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

Constant Summary collapse

VERSION =
"0.5.2"

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)


832
833
834
# File 'lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb', line 832

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.



839
840
841
842
843
844
845
846
847
848
# File 'lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb', line 839

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.



941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
# File 'lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb', line 941

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.



885
886
887
888
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
# File 'lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb', line 885

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.



963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
# File 'lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb', line 963

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