archive-zip 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ module Archive; class Zip;
2
+ # Archive::Zip::Error is the base class of all archive-related errors raised
3
+ # by the Archive::Zip library.
4
+ class Error < StandardError; end
5
+
6
+ # Archive::Zip::EntryError is raised when there is an error while processing
7
+ # ZIP archive entries.
8
+ class EntryError < Error; end
9
+
10
+ # Archive::Zip::ExtraFieldError is raised when there is an error while
11
+ # processing an extra field in a ZIP archive entry.
12
+ class ExtraFieldError < Error; end
13
+
14
+ # Archive::Zip::IOError is raised in various places where either an IOError is
15
+ # raised or an operation on an Archive::Zip object is disallowed because of
16
+ # the state of the object.
17
+ class IOError < Error; end
18
+
19
+ # Archive::Zip::UnzipError is raised when attempting to extract an archive
20
+ # fails but no more exact error class exists for reporting the error.
21
+ class UnzipError < Error; end
22
+ end; end
@@ -0,0 +1,23 @@
1
+ module Archive; class Zip
2
+ module ExtraField
3
+ # A Hash used to map extra field header identifiers to extra field classes.
4
+ EXTRA_FIELDS = {}
5
+
6
+ # Returns an instance of an extra field class by selecting the class using
7
+ # _header_id_ and passing _data_ to the class' _parse_ method. If there is
8
+ # no mapping from a given value of _header_id_ to an extra field class, an
9
+ # instance of Archive::Zip::Entry::ExtraField::Raw is returned.
10
+ def self.parse(header_id, data)
11
+ if EXTRA_FIELDS.has_key?(header_id) then
12
+ EXTRA_FIELDS[header_id].parse(data)
13
+ else
14
+ Raw.parse(header_id, data)
15
+ end
16
+ end
17
+ end
18
+ end; end
19
+
20
+ # Load the standard extra field classes.
21
+ require 'archive/zip/extrafield/extendedtimestamp'
22
+ require 'archive/zip/extrafield/raw'
23
+ require 'archive/zip/extrafield/unix'
@@ -0,0 +1,101 @@
1
+ require 'archive/zip/error'
2
+
3
+ module Archive; class Zip; module ExtraField
4
+ # Archive::Zip::Entry::ExtraField::ExtendedTimestamp represents an extra field
5
+ # which optionally contains the last modified time, last accessed time, and
6
+ # file creation time for a ZIP archive entry and stored in a Unix time format
7
+ # (seconds since the epoc).
8
+ class ExtendedTimestamp
9
+ # The identifier reserved for this extra field type.
10
+ ID = 0x5455
11
+
12
+ # Register this extra field for use.
13
+ EXTRA_FIELDS[ID] = self
14
+
15
+ # This method signature is part of the interface contract expected by
16
+ # Archive::Zip::Entry for extra field objects.
17
+ #
18
+ # Parses _data_ which is expected to be a String formatted according to the
19
+ # documentation provided with InfoZip's sources.
20
+ #
21
+ # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data.
22
+ def self.parse(data)
23
+ unless data.size == 5 || data.size == 9 || data.size == 13 then
24
+ raise Zip::ExtraFieldError,
25
+ "invalid size for extended timestamp: #{data.size}"
26
+ end
27
+ flags, *times = data.unpack('C' + 'V' * ((data.size - 1) / 4))
28
+ mtime = nil
29
+ atime = nil
30
+ crtime = nil
31
+ if flags & 0b001 != 0 then
32
+ if times.size == 0 then
33
+ # Report an error if the flags indicate that the last modified time
34
+ # field should be present when it is not.
35
+ raise Zip::ExtraFieldError,
36
+ 'corrupt extended timestamp: last modified time field not present'
37
+ end
38
+ mtime = Time.at(times.shift)
39
+ end
40
+ if flags & 0b010 != 0 then
41
+ # HACK:
42
+ # InfoZip does not follow their own documentation for this extra field
43
+ # when creating one for an entry's central file record. They flag that
44
+ # the atime field should be present when it is not. Ignore the flag in
45
+ # that case.
46
+ if times.size > 0 then
47
+ atime = Time.at(times.shift)
48
+ end
49
+ end
50
+ if flags & 0b100 != 0 then
51
+ if times.size == 0 then
52
+ # Report an error if the flags indicate that the file creation time
53
+ # field should be present when it is not.
54
+ raise Zip::ExtraFieldError,
55
+ 'corrupt extended timestamp: file creation time field not present'
56
+ end
57
+ crtime = Time.at(times.shift)
58
+ end
59
+ new(mtime, atime, crtime)
60
+ end
61
+
62
+ # Creates a new instance of this class. _mtime_, _atime_, and _crtime_
63
+ # should be Time instances or +nil+. When set to +nil+ the field is
64
+ # considered to be unset and will not be stored in the archive.
65
+ def initialize(mtime, atime, crtime)
66
+ @mtime = mtime
67
+ @atime = atime
68
+ @crtime = crtime
69
+ end
70
+
71
+ # The last modified time for an entry.
72
+ attr_accessor :mtime
73
+ # The last accessed time for an entry.
74
+ attr_accessor :atime
75
+ # The creation time for an entry.
76
+ attr_accessor :crtime
77
+
78
+ # This method signature is part of the interface contract expected by
79
+ # Archive::Zip::Entry for extra field objects.
80
+ #
81
+ # Returns a String suitable to writing to a ZIP archive file which contains
82
+ # the data for this object.
83
+ def dump
84
+ flags = 0
85
+ times = []
86
+ unless mtime.nil? then
87
+ flags |= 0b001
88
+ times << mtime.to_i
89
+ end
90
+ unless atime.nil? then
91
+ flags |= 0b010
92
+ times << atime.to_i
93
+ end
94
+ unless crtime.nil? then
95
+ flags |= 0b100
96
+ times << crtime.to_i
97
+ end
98
+ ([ID, 4 * times.size + 1, flags] + times).pack('vvC' + 'V' * times.size)
99
+ end
100
+ end
101
+ end; end; end
@@ -0,0 +1,32 @@
1
+ module Archive; class Zip; module ExtraField
2
+ # Archive::Zip::Entry::ExtraField::Raw represents an unknown extra field. It
3
+ # is used to store extra fields the Archive::Zip library does not directly
4
+ # support.
5
+ class Raw
6
+ # Simply stores _header_id_ and _data_ for later reproduction by #dump.
7
+ # This is essentially and alias for #new.
8
+ def self.parse(header_id, data)
9
+ new(header_id, data)
10
+ end
11
+
12
+ # Simply stores _header_id_ and _data_ for later reproduction by #dump.
13
+ def initialize(header_id, data)
14
+ @header_id = header_id
15
+ @data = data
16
+ end
17
+
18
+ # Returns the header ID for this ExtraField.
19
+ attr_reader :header_id
20
+ # Returns the data contained within this ExtraField.
21
+ attr_reader :data
22
+
23
+ # This method signature is part of the interface contract expected by
24
+ # Archive::Zip::Entry for extra field objects.
25
+ #
26
+ # Returns a String suitable to writing to a ZIP archive file which contains
27
+ # the data for this object.
28
+ def dump
29
+ [header_id, @data.size].pack('vv') + @data
30
+ end
31
+ end
32
+ end; end; end
@@ -0,0 +1,101 @@
1
+ require 'archive/zip/error'
2
+
3
+ module Archive; class Zip; module ExtraField
4
+ # Archive::Zip::Entry::ExtraField::Unix represents an extra field which
5
+ # contains the last modified time, last accessed time, user name, and group
6
+ # name for a ZIP archive entry. Times are in Unix time format (seconds since
7
+ # the epoc).
8
+ #
9
+ # This class also optionally stores either major and minor numbers for devices
10
+ # or a link target for either hard or soft links. Which is in use when given
11
+ # and instance of this class depends upon the external file attributes for the
12
+ # ZIP archive entry associated with this extra field.
13
+ class Unix
14
+ # The identifier reserved for this extra field type.
15
+ ID = 0x000d
16
+
17
+ # Register this extra field for use.
18
+ EXTRA_FIELDS[ID] = self
19
+
20
+ # This method signature is part of the interface contract expected by
21
+ # Archive::Zip::Entry for extra field objects.
22
+ #
23
+ # Parses _data_ which is expected to be a String formatted according to the
24
+ # official ZIP specification.
25
+ #
26
+ # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data.
27
+ def self.parse(data)
28
+ unless data.length >= 12 then
29
+ raise Zip::ExtraFieldError, "invalid size for Unix data: #{data.size}"
30
+ end
31
+ atime, mtime, uid, gid, rest = data.unpack('VVvva')
32
+ new(Time.at(mtime), Time.at(atime), uid, gid, rest)
33
+ end
34
+
35
+ # Creates a new instance of this class. _mtime_ and _atime_ should be Time
36
+ # instances. _uid_ and _gid_ should be user and group names as strings
37
+ # respectively. _data_ should be a string containing either major and minor
38
+ # device numbers consecutively packed as little endian, 4-byte, unsigned
39
+ # integers (see the _V_ directive of Array#pack) or a path to use as a link
40
+ # target.
41
+ def initialize(mtime, atime, uid, gid, data = '')
42
+ @mtime = mtime
43
+ @atime = atime
44
+ @uid = uid
45
+ @gid = gid
46
+ @data = data
47
+ end
48
+
49
+ # A Time object representing the last accessed time for an entry.
50
+ attr_accessor :atime
51
+ # A Time object representing the last modified time for an entry.
52
+ attr_accessor :mtime
53
+ # An integer representing the user ownership for an entry.
54
+ attr_accessor :uid
55
+ # An integer representing the group ownership for an entry.
56
+ attr_accessor :gid
57
+
58
+ # Attempts to return a two element array representing the major and minor
59
+ # device numbers which may be stored in the variable data section of this
60
+ # object.
61
+ def device_numbers
62
+ @data.unpack('VV')
63
+ end
64
+
65
+ # Takes a two element array containing major and minor device numbers and
66
+ # stores the numbers into the variable data section of this object.
67
+ def device_numbers=(major_minor)
68
+ @data = major_minor.pack('VV')
69
+ end
70
+
71
+ # Attempts to return a string representing the path of a file which is
72
+ # either a symlink or hard link target which may be stored in the variable
73
+ # data section of this object.
74
+ def link_target
75
+ @data
76
+ end
77
+
78
+ # Takes a string containing the path to a file which is either a symlink or
79
+ # a hardlink target and stores it in the variable data section of this
80
+ # object.
81
+ def link_target=(link_target)
82
+ @data = link_target
83
+ end
84
+
85
+ # This method signature is part of the interface contract expected by
86
+ # Archive::Zip::Entry for extra field objects.
87
+ #
88
+ # Returns a String suitable to writing to a ZIP archive file which contains
89
+ # the data for this object.
90
+ def dump
91
+ [
92
+ ID,
93
+ 12 + @data.size,
94
+ @atime.to_i,
95
+ @mtime.to_i,
96
+ @uid,
97
+ @gid
98
+ ].pack('vvVVvv') + @data
99
+ end
100
+ end
101
+ end; end; end
@@ -0,0 +1,8 @@
1
+ require 'archive/zip'
2
+
3
+ FileUtils.rm_f('export.zip')
4
+ FileUtils.rm_rf('extract_test')
5
+
6
+ Archive::Zip.archive('export.zip', 'test/data/.', :symlinks => false, :ignore_error => true)
7
+ #Archive::Zip.archive('export.zip', 'test/data/.', :symlinks => true, :exclude => lambda { |e| e.file? })
8
+ Archive::Zip.extract('export.zip', 'extract_test', :symlinks => true, :exclude => lambda { |e| e.symlink? })
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: archive-zip
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Bopp
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-10 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: io-like
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.0
24
+ version:
25
+ description: Archive::Zip provides a simple Ruby-esque interface to creating, extracting, and updating ZIP archives. This implementation is 100% Ruby and loosely modeled on the archive creation and extraction capabilities of InfoZip's zip and unzip tools.
26
+ email: jeremy at bopp dot net
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - CONTRIBUTORS
33
+ - HACKING
34
+ - LICENSE
35
+ - GPL
36
+ - LEGAL
37
+ - NEWS
38
+ - README
39
+ files:
40
+ - lib/archive/support/io-like.rb
41
+ - lib/archive/support/io.rb
42
+ - lib/archive/support/iowindow.rb
43
+ - lib/archive/support/stringio.rb
44
+ - lib/archive/support/time.rb
45
+ - lib/archive/support/zlib.rb
46
+ - lib/archive/zip/codec/deflate.rb
47
+ - lib/archive/zip/codec/store.rb
48
+ - lib/archive/zip/codec.rb
49
+ - lib/archive/zip/datadescriptor.rb
50
+ - lib/archive/zip/entry.rb
51
+ - lib/archive/zip/error.rb
52
+ - lib/archive/zip/extrafield/extendedtimestamp.rb
53
+ - lib/archive/zip/extrafield/raw.rb
54
+ - lib/archive/zip/extrafield/unix.rb
55
+ - lib/archive/zip/extrafield.rb
56
+ - lib/archive/zip.rb
57
+ - test/test_archive.rb
58
+ - CONTRIBUTORS
59
+ - HACKING
60
+ - LICENSE
61
+ - GPL
62
+ - LEGAL
63
+ - NEWS
64
+ - README
65
+ - MANIFEST
66
+ has_rdoc: true
67
+ homepage: http://archive-zip.rubyforge.org
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --title
71
+ - Archive::Zip Documentation
72
+ - --charset
73
+ - utf-8
74
+ - --line-numbers
75
+ - --inline-source
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.1
83
+ version:
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ version:
90
+ requirements: []
91
+
92
+ rubyforge_project: archive-zip
93
+ rubygems_version: 1.2.0
94
+ signing_key:
95
+ specification_version: 2
96
+ summary: Simple, extensible, pure Ruby ZIP archive support.
97
+ test_files: []
98
+