archive-zip 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|