archive-zip 0.3.0 → 0.4.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.
Files changed (132) hide show
  1. data/HACKING +25 -42
  2. data/NEWS +25 -0
  3. data/README +2 -2
  4. data/Rakefile +202 -0
  5. data/TODO +5 -0
  6. data/default.mspec +8 -0
  7. data/lib/archive/support/binary_stringio.rb +23 -0
  8. data/lib/archive/support/integer.rb +13 -0
  9. data/lib/archive/support/io-like.rb +3 -1
  10. data/lib/archive/support/ioextensions.rb +16 -0
  11. data/lib/archive/support/iowindow.rb +10 -18
  12. data/lib/archive/support/time.rb +2 -0
  13. data/lib/archive/support/zlib.rb +298 -71
  14. data/lib/archive/zip.rb +161 -139
  15. data/lib/archive/zip/codec.rb +2 -0
  16. data/lib/archive/zip/codec/deflate.rb +59 -11
  17. data/lib/archive/zip/codec/null_encryption.rb +75 -14
  18. data/lib/archive/zip/codec/store.rb +75 -26
  19. data/lib/archive/zip/codec/traditional_encryption.rb +146 -35
  20. data/lib/archive/zip/data_descriptor.rb +6 -4
  21. data/lib/archive/zip/entry.rb +184 -132
  22. data/lib/archive/zip/error.rb +2 -0
  23. data/lib/archive/zip/extra_field.rb +20 -6
  24. data/lib/archive/zip/extra_field/extended_timestamp.rb +141 -60
  25. data/lib/archive/zip/extra_field/raw.rb +70 -12
  26. data/lib/archive/zip/extra_field/unix.rb +58 -16
  27. data/lib/archive/zip/version.rb +6 -0
  28. data/spec/archive/zip/codec/deflate/compress/checksum_spec.rb +42 -0
  29. data/spec/archive/zip/codec/deflate/compress/close_spec.rb +44 -0
  30. data/spec/archive/zip/codec/deflate/compress/crc32_spec.rb +21 -0
  31. data/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb +67 -0
  32. data/spec/archive/zip/codec/deflate/compress/new_spec.rb +37 -0
  33. data/spec/archive/zip/codec/deflate/compress/open_spec.rb +46 -0
  34. data/spec/archive/zip/codec/deflate/compress/write_spec.rb +109 -0
  35. data/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb +18 -0
  36. data/spec/archive/zip/codec/deflate/decompress/close_spec.rb +33 -0
  37. data/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb +18 -0
  38. data/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb +67 -0
  39. data/spec/archive/zip/codec/deflate/decompress/new_spec.rb +14 -0
  40. data/spec/archive/zip/codec/deflate/decompress/open_spec.rb +27 -0
  41. data/spec/archive/zip/codec/deflate/fixtures/classes.rb +25 -0
  42. data/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin +1 -0
  43. data/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin +0 -0
  44. data/spec/archive/zip/codec/deflate/fixtures/raw_file.txt +10 -0
  45. data/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb +33 -0
  46. data/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb +14 -0
  47. data/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb +27 -0
  48. data/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb +24 -0
  49. data/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb +25 -0
  50. data/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb +57 -0
  51. data/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb +21 -0
  52. data/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb +33 -0
  53. data/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb +14 -0
  54. data/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb +27 -0
  55. data/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb +26 -0
  56. data/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb +50 -0
  57. data/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb +29 -0
  58. data/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb +29 -0
  59. data/spec/archive/zip/codec/null_encryption/fixtures/classes.rb +12 -0
  60. data/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt +10 -0
  61. data/spec/archive/zip/codec/store/compress/close_spec.rb +33 -0
  62. data/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb +68 -0
  63. data/spec/archive/zip/codec/store/compress/new_spec.rb +14 -0
  64. data/spec/archive/zip/codec/store/compress/open_spec.rb +27 -0
  65. data/spec/archive/zip/codec/store/compress/rewind_spec.rb +26 -0
  66. data/spec/archive/zip/codec/store/compress/seek_spec.rb +50 -0
  67. data/spec/archive/zip/codec/store/compress/tell_spec.rb +29 -0
  68. data/spec/archive/zip/codec/store/compress/write_spec.rb +29 -0
  69. data/spec/archive/zip/codec/store/decompress/close_spec.rb +33 -0
  70. data/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb +68 -0
  71. data/spec/archive/zip/codec/store/decompress/new_spec.rb +14 -0
  72. data/spec/archive/zip/codec/store/decompress/open_spec.rb +27 -0
  73. data/spec/archive/zip/codec/store/decompress/read_spec.rb +24 -0
  74. data/spec/archive/zip/codec/store/decompress/rewind_spec.rb +25 -0
  75. data/spec/archive/zip/codec/store/decompress/seek_spec.rb +57 -0
  76. data/spec/archive/zip/codec/store/decompress/tell_spec.rb +21 -0
  77. data/spec/archive/zip/codec/store/fixtures/classes.rb +12 -0
  78. data/spec/archive/zip/codec/store/fixtures/raw_file.txt +10 -0
  79. data/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb +64 -0
  80. data/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb +18 -0
  81. data/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb +39 -0
  82. data/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb +126 -0
  83. data/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb +38 -0
  84. data/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb +82 -0
  85. data/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb +25 -0
  86. data/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb +64 -0
  87. data/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb +18 -0
  88. data/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb +39 -0
  89. data/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb +41 -0
  90. data/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb +75 -0
  91. data/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb +42 -0
  92. data/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb +127 -0
  93. data/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb +27 -0
  94. data/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin +0 -0
  95. data/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt +10 -0
  96. data/spec/binary_stringio/new_spec.rb +34 -0
  97. data/spec/binary_stringio/set_encoding_spec.rb +14 -0
  98. data/spec/ioextensions/read_exactly_spec.rb +50 -0
  99. data/spec/zlib/fixtures/classes.rb +65 -0
  100. data/spec/zlib/fixtures/compressed_file.bin +1 -0
  101. data/spec/zlib/fixtures/compressed_file_gzip.bin +0 -0
  102. data/spec/zlib/fixtures/compressed_file_huffman.bin +2 -0
  103. data/spec/zlib/fixtures/compressed_file_minmem.bin +0 -0
  104. data/spec/zlib/fixtures/compressed_file_minwin.bin +1 -0
  105. data/spec/zlib/fixtures/compressed_file_nocomp.bin +0 -0
  106. data/spec/zlib/fixtures/compressed_file_raw.bin +1 -0
  107. data/spec/zlib/fixtures/raw_file.txt +10 -0
  108. data/spec/zlib/zreader/checksum_spec.rb +40 -0
  109. data/spec/zlib/zreader/close_spec.rb +14 -0
  110. data/spec/zlib/zreader/compressed_size_spec.rb +18 -0
  111. data/spec/zlib/zreader/new_spec.rb +41 -0
  112. data/spec/zlib/zreader/open_spec.rb +49 -0
  113. data/spec/zlib/zreader/read_spec.rb +47 -0
  114. data/spec/zlib/zreader/rewind_spec.rb +23 -0
  115. data/spec/zlib/zreader/seek_spec.rb +55 -0
  116. data/spec/zlib/zreader/tell_spec.rb +21 -0
  117. data/spec/zlib/zreader/uncompressed_size_spec.rb +18 -0
  118. data/spec/zlib/zwriter/checksum_spec.rb +41 -0
  119. data/spec/zlib/zwriter/close_spec.rb +14 -0
  120. data/spec/zlib/zwriter/compressed_size_spec.rb +19 -0
  121. data/spec/zlib/zwriter/new_spec.rb +64 -0
  122. data/spec/zlib/zwriter/open_spec.rb +68 -0
  123. data/spec/zlib/zwriter/rewind_spec.rb +26 -0
  124. data/spec/zlib/zwriter/seek_spec.rb +54 -0
  125. data/spec/zlib/zwriter/tell_spec.rb +29 -0
  126. data/spec/zlib/zwriter/uncompressed_size_spec.rb +19 -0
  127. data/spec/zlib/zwriter/write_spec.rb +28 -0
  128. data/spec_helper.rb +49 -0
  129. metadata +296 -74
  130. data/MANIFEST +0 -27
  131. data/lib/archive/support/io.rb +0 -14
  132. data/lib/archive/support/stringio.rb +0 -22
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  module Archive; class Zip;
2
4
  # Archive::Zip::Error is the base class of all archive-related errors raised
3
5
  # by the Archive::Zip library.
@@ -1,17 +1,31 @@
1
+ # encoding: UTF-8
2
+
1
3
  module Archive; class Zip
2
4
  module ExtraField
3
5
  # A Hash used to map extra field header identifiers to extra field classes.
4
6
  EXTRA_FIELDS = {}
5
7
 
6
8
  # 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)
9
+ # _header_id_ and passing _data_ to the class' _parse_central_ method. If
10
+ # there is no mapping from a given value of _header_id_ to an extra field
11
+ # class, an instance of Archive::Zip::Entry::ExtraField::Raw is returned.
12
+ def self.parse_central(header_id, data)
13
+ if EXTRA_FIELDS.has_key?(header_id) then
14
+ EXTRA_FIELDS[header_id].parse_central(data)
15
+ else
16
+ Raw.parse_central(header_id, data)
17
+ end
18
+ end
19
+
20
+ # Returns an instance of an extra field class by selecting the class using
21
+ # _header_id_ and passing _data_ to the class' _parse_local_ method. If
22
+ # there is no mapping from a given value of _header_id_ to an extra field
23
+ # class, an instance of Archive::Zip::Entry::ExtraField::Raw is returned.
24
+ def self.parse_local(header_id, data)
11
25
  if EXTRA_FIELDS.has_key?(header_id) then
12
- EXTRA_FIELDS[header_id].parse(data)
26
+ EXTRA_FIELDS[header_id].parse_local(data)
13
27
  else
14
- Raw.parse(header_id, data)
28
+ Raw.parse_local(header_id, data)
15
29
  end
16
30
  end
17
31
  end
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'archive/zip/error'
2
4
 
3
5
  module Archive; class Zip; module ExtraField
@@ -12,90 +14,169 @@ module Archive; class Zip; module ExtraField
12
14
  # Register this extra field for use.
13
15
  EXTRA_FIELDS[ID] = self
14
16
 
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.
17
+ class << self
18
+ # This method signature is part of the interface contract expected by
19
+ # Archive::Zip::Entry for extra field objects.
20
+ #
21
+ # Parses _data_ which is expected to be a String formatted according to
22
+ # the documentation provided with InfoZip's sources.
23
+ #
24
+ # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data.
25
+ def parse_central(data)
26
+ unless data.size == 5 || data.size == 9 || data.size == 13 then
35
27
  raise Zip::ExtraFieldError,
36
- 'corrupt extended timestamp: last modified time field not present'
28
+ "invalid size for extended timestamp: #{data.size}"
37
29
  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)
30
+ flags, *times = data.unpack('CV*')
31
+ mtime = nil
32
+ atime = nil
33
+ crtime = nil
34
+ if flags & 0b001 != 0 then
35
+ if times.size == 0 then
36
+ # Report an error if the flags indicate that the last modified time
37
+ # field should be present when it is not.
38
+ raise Zip::ExtraFieldError,
39
+ 'corrupt extended timestamp: last modified time field not present'
40
+ end
41
+ mtime = Time.at(times.shift)
42
+ end
43
+ if flags & 0b010 != 0 then
44
+ # If parsing the central file record version of this field, this flag
45
+ # may be set without having the corresponding time value.
46
+ # Use the time value if available, but ignore it if it's missing.
47
+ if times.size > 0 then
48
+ atime = Time.at(times.shift)
49
+ end
50
+ end
51
+ if flags & 0b100 != 0 then
52
+ # If parsing the central file record version of this field, this flag
53
+ # may be set without having the corresponding time value.
54
+ # Use the time value if available, but ignore it if it's missing.
55
+ if times.size > 0 then
56
+ crtime = Time.at(times.shift)
57
+ end
48
58
  end
59
+ new(mtime, atime, crtime)
49
60
  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.
61
+
62
+ # This method signature is part of the interface contract expected by
63
+ # Archive::Zip::Entry for extra field objects.
64
+ #
65
+ # Parses _data_ which is expected to be a String formatted according to
66
+ # the documentation provided with InfoZip's sources.
67
+ #
68
+ # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data.
69
+ def parse_local(data)
70
+ unless data.size == 5 || data.size == 9 || data.size == 13 then
54
71
  raise Zip::ExtraFieldError,
55
- 'corrupt extended timestamp: file creation time field not present'
72
+ "invalid size for extended timestamp: #{data.size}"
73
+ end
74
+ flags, *times = data.unpack('CV*')
75
+ mtime = nil
76
+ atime = nil
77
+ crtime = nil
78
+ if flags & 0b001 != 0 then
79
+ if times.size == 0 then
80
+ # Report an error if the flags indicate that the last modified time
81
+ # field should be present when it is not.
82
+ raise Zip::ExtraFieldError,
83
+ 'corrupt extended timestamp: last modified time field not present'
84
+ end
85
+ mtime = Time.at(times.shift)
86
+ end
87
+ if flags & 0b010 != 0 then
88
+ if times.size == 0 then
89
+ # Report an error if the flags indicate that the last modified time
90
+ # field should be present when it is not.
91
+ raise Zip::ExtraFieldError,
92
+ 'corrupt extended timestamp: last accessed time field not present'
93
+ end
94
+ atime = Time.at(times.shift)
95
+ end
96
+ if flags & 0b100 != 0 then
97
+ if times.size == 0 then
98
+ # Report an error if the flags indicate that the file creation time
99
+ # field should be present when it is not.
100
+ raise Zip::ExtraFieldError,
101
+ 'corrupt extended timestamp: file creation time field not present'
102
+ end
103
+ crtime = Time.at(times.shift)
56
104
  end
57
- crtime = Time.at(times.shift)
105
+ new(mtime, atime, crtime)
58
106
  end
59
- new(mtime, atime, crtime)
60
107
  end
61
108
 
62
109
  # Creates a new instance of this class. _mtime_, _atime_, and _crtime_
63
110
  # should be Time instances or +nil+. When set to +nil+ the field is
64
111
  # considered to be unset and will not be stored in the archive.
65
112
  def initialize(mtime, atime, crtime)
66
- @mtime = mtime
67
- @atime = atime
68
- @crtime = crtime
113
+ @header_id = ID
114
+ self.mtime = mtime unless mtime.nil?
115
+ self.atime = atime unless atime.nil?
116
+ self.crtime = crtime unless crtime.nil?
69
117
  end
70
118
 
71
- # The last modified time for an entry.
119
+ # Returns the header ID for this ExtraField.
120
+ attr_reader :header_id
121
+ # The last modified time for an entry. Set to either a Time instance or
122
+ # +nil+.
72
123
  attr_accessor :mtime
73
- # The last accessed time for an entry.
124
+ # The last accessed time for an entry. Set to either a Time instance or
125
+ # +nil+.
74
126
  attr_accessor :atime
75
- # The creation time for an entry.
127
+ # The creation time for an entry. Set to either a Time instance or +nil+.
76
128
  attr_accessor :crtime
77
129
 
78
130
  # This method signature is part of the interface contract expected by
79
131
  # Archive::Zip::Entry for extra field objects.
80
132
  #
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
133
+ # Merges the attributes of _other_ into this object and returns +self+.
134
+ #
135
+ # Raises ArgumentError if _other_ is not the same class as this object.
136
+ def merge(other)
137
+ if self.class != other.class then
138
+ raise ArgumentError, "#{self.class} is not the same as #{other.class}"
97
139
  end
140
+
141
+ @mtime = other.mtime unless other.mtime.nil?
142
+ @atime = other.atime unless other.atime.nil?
143
+ @crtime = other.crtime unless other.crtime.nil?
144
+
145
+ self
146
+ end
147
+
148
+ # This method signature is part of the interface contract expected by
149
+ # Archive::Zip::Entry for extra field objects.
150
+ #
151
+ # Returns a String suitable to writing to a central file record in a ZIP
152
+ # archive file which contains the data for this object.
153
+ def dump_central
154
+ times = []
155
+ times << mtime.to_i unless mtime.nil?
98
156
  ([ID, 4 * times.size + 1, flags] + times).pack('vvC' + 'V' * times.size)
99
157
  end
158
+
159
+ # This method signature is part of the interface contract expected by
160
+ # Archive::Zip::Entry for extra field objects.
161
+ #
162
+ # Returns a String suitable to writing to a local file record in a ZIP
163
+ # archive file which contains the data for this object.
164
+ def dump_local
165
+ times = []
166
+ times << mtime.to_i unless mtime.nil?
167
+ times << atime.to_i unless atime.nil?
168
+ times << crtime.to_i unless crtime.nil?
169
+ ([ID, 4 * times.size + 1, flags] + times).pack('vvC' + 'V' * times.size)
170
+ end
171
+
172
+ private
173
+
174
+ def flags
175
+ flags = 0
176
+ flags |= 0b001 unless mtime.nil?
177
+ flags |= 0b010 unless atime.nil?
178
+ flags |= 0b100 unless crtime.nil?
179
+ flags
180
+ end
100
181
  end
101
182
  end; end; end
@@ -1,32 +1,90 @@
1
+ # encoding: UTF-8
2
+
1
3
  module Archive; class Zip; module ExtraField
2
4
  # Archive::Zip::Entry::ExtraField::Raw represents an unknown extra field. It
3
5
  # is used to store extra fields the Archive::Zip library does not directly
4
6
  # support.
7
+ #
8
+ # Do not use this class directly. Define a new class which supports the extra
9
+ # field of interest directly instead.
5
10
  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)
11
+ class << self
12
+ # Simply stores _header_id_ and _data_ for later reproduction by
13
+ # #dump_central.
14
+ # This is essentially and alias for #new.
15
+ def parse_central(header_id, data)
16
+ new(header_id, data, true)
17
+ end
18
+
19
+ # Simply stores _header_id_ and _data_ for later reproduction by
20
+ # #dump_local.
21
+ # This is essentially and alias for #new.
22
+ def parse_local(header_id, data)
23
+ new(header_id, data, false)
24
+ end
10
25
  end
11
26
 
12
- # Simply stores _header_id_ and _data_ for later reproduction by #dump.
13
- def initialize(header_id, data)
27
+ # Simply stores _header_id_ and _data_ for later reproduction by
28
+ # #dump_central or #dump_local. _central_record_ indicates that this field
29
+ # resides in the central file record for an entry when +true+. When
30
+ # +false+, it indicates that this field resides in the local file record for
31
+ # an entry.
32
+ def initialize(header_id, data, central_record)
14
33
  @header_id = header_id
15
- @data = data
34
+ @central_record_data = []
35
+ @local_record_data = []
36
+ if central_record then
37
+ @central_record_data << data
38
+ else
39
+ @local_record_data << data
40
+ end
16
41
  end
17
42
 
18
43
  # Returns the header ID for this ExtraField.
19
44
  attr_reader :header_id
20
45
  # Returns the data contained within this ExtraField.
21
- attr_reader :data
46
+ attr_reader :central_record_data
47
+ attr_reader :local_record_data
48
+
49
+ # This method signature is part of the interface contract expected by
50
+ # Archive::Zip::Entry for extra field objects.
51
+ #
52
+ # Merges the attributes of _other_ into this object and returns +self+.
53
+ #
54
+ # Raises ArgumentError if _other_ does not have the same header ID as this
55
+ # object.
56
+ def merge(other)
57
+ if header_id != other.header_id then
58
+ raise ArgumentError,
59
+ "Header ID mismatch: #{header_id} != #{other.header_id}"
60
+ end
61
+
62
+ @central_record_data += other.central_record_data
63
+ @local_record_data += other.local_record_data
64
+
65
+ self
66
+ end
67
+
68
+ # This method signature is part of the interface contract expected by
69
+ # Archive::Zip::Entry for extra field objects.
70
+ #
71
+ # Returns a String suitable to writing to a central file record in a ZIP
72
+ # archive file which contains the data for this object.
73
+ def dump_central
74
+ @central_record_data.collect do |data|
75
+ [header_id, data.size].pack('vv') + data
76
+ end
77
+ end
22
78
 
23
79
  # This method signature is part of the interface contract expected by
24
80
  # Archive::Zip::Entry for extra field objects.
25
81
  #
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
82
+ # Returns a String suitable to writing to a local file record in a ZIP
83
+ # archive file which contains the data for this object.
84
+ def dump_local
85
+ @local_record_data.collect do |data|
86
+ [header_id, data.size].pack('vv') + data
87
+ end
30
88
  end
31
89
  end
32
90
  end; end; end
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'archive/zip/error'
2
4
 
3
5
  module Archive; class Zip; module ExtraField
@@ -17,28 +19,32 @@ module Archive; class Zip; module ExtraField
17
19
  # Register this extra field for use.
18
20
  EXTRA_FIELDS[ID] = self
19
21
 
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}"
22
+ class << self
23
+ # This method signature is part of the interface contract expected by
24
+ # Archive::Zip::Entry for extra field objects.
25
+ #
26
+ # Parses _data_ which is expected to be a String formatted according to
27
+ # the official ZIP specification.
28
+ #
29
+ # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data.
30
+ def parse_central(data)
31
+ unless data.length >= 12 then
32
+ raise Zip::ExtraFieldError, "invalid size for Unix data: #{data.size}"
33
+ end
34
+ atime, mtime, uid, gid, rest = data.unpack('VVvva')
35
+ new(Time.at(mtime), Time.at(atime), uid, gid, rest)
30
36
  end
31
- atime, mtime, uid, gid, rest = data.unpack('VVvva')
32
- new(Time.at(mtime), Time.at(atime), uid, gid, rest)
37
+ alias :parse_local :parse_central
33
38
  end
34
39
 
35
40
  # 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
41
+ # instances. _uid_ and _gid_ should be user and group IDs as Integers
37
42
  # respectively. _data_ should be a string containing either major and minor
38
43
  # device numbers consecutively packed as little endian, 4-byte, unsigned
39
44
  # integers (see the _V_ directive of Array#pack) or a path to use as a link
40
45
  # target.
41
46
  def initialize(mtime, atime, uid, gid, data = '')
47
+ @header_id = ID
42
48
  @mtime = mtime
43
49
  @atime = atime
44
50
  @uid = uid
@@ -46,6 +52,8 @@ module Archive; class Zip; module ExtraField
46
52
  @data = data
47
53
  end
48
54
 
55
+ # Returns the header ID for this ExtraField.
56
+ attr_reader :header_id
49
57
  # A Time object representing the last accessed time for an entry.
50
58
  attr_accessor :atime
51
59
  # A Time object representing the last modified time for an entry.
@@ -85,9 +93,38 @@ module Archive; class Zip; module ExtraField
85
93
  # This method signature is part of the interface contract expected by
86
94
  # Archive::Zip::Entry for extra field objects.
87
95
  #
88
- # Returns a String suitable to writing to a ZIP archive file which contains
89
- # the data for this object.
90
- def dump
96
+ # Merges the attributes of _other_ into this object and returns +self+.
97
+ #
98
+ # Raises ArgumentError if _other_ is not the same class as this object.
99
+ def merge(other)
100
+ if self.class != other.class then
101
+ raise ArgumentError, "#{self.class} is not the same as #{other.class}"
102
+ end
103
+
104
+ @atime = other.atime
105
+ @mtime = other.mtime
106
+ @uid = other.uid
107
+ @gid = other.gid
108
+ @data = other.data
109
+
110
+ self
111
+ end
112
+
113
+ # This method signature is part of the interface contract expected by
114
+ # Archive::Zip::Entry for extra field objects.
115
+ #
116
+ # Returns a String suitable to writing to a central file record in a ZIP
117
+ # archive file which contains the data for this object.
118
+ def dump_central
119
+ ''
120
+ end
121
+
122
+ # This method signature is part of the interface contract expected by
123
+ # Archive::Zip::Entry for extra field objects.
124
+ #
125
+ # Returns a String suitable to writing to a local file record in a ZIP
126
+ # archive file which contains the data for this object.
127
+ def dump_local
91
128
  [
92
129
  ID,
93
130
  12 + @data.size,
@@ -97,5 +134,10 @@ module Archive; class Zip; module ExtraField
97
134
  @gid
98
135
  ].pack('vvVVvv') + @data
99
136
  end
137
+ alias :dump_local :dump_central
138
+
139
+ protected
140
+
141
+ attr_reader :data
100
142
  end
101
143
  end; end; end