archive-zip 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CONTRIBUTORS +13 -0
- data/GPL +676 -0
- data/HACKING +122 -0
- data/LEGAL +8 -0
- data/LICENSE +57 -0
- data/MANIFEST +26 -0
- data/NEWS +22 -0
- data/README +130 -0
- data/lib/archive/support/io-like.rb +12 -0
- data/lib/archive/support/io.rb +14 -0
- data/lib/archive/support/iowindow.rb +123 -0
- data/lib/archive/support/stringio.rb +22 -0
- data/lib/archive/support/time.rb +85 -0
- data/lib/archive/support/zlib.rb +211 -0
- data/lib/archive/zip.rb +643 -0
- data/lib/archive/zip/codec.rb +30 -0
- data/lib/archive/zip/codec/deflate.rb +206 -0
- data/lib/archive/zip/codec/store.rb +241 -0
- data/lib/archive/zip/datadescriptor.rb +54 -0
- data/lib/archive/zip/entry.rb +991 -0
- data/lib/archive/zip/error.rb +22 -0
- data/lib/archive/zip/extrafield.rb +23 -0
- data/lib/archive/zip/extrafield/extendedtimestamp.rb +101 -0
- data/lib/archive/zip/extrafield/raw.rb +32 -0
- data/lib/archive/zip/extrafield/unix.rb +101 -0
- data/test/test_archive.rb +8 -0
- metadata +98 -0
@@ -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
|
+
|