archive-zip 0.3.0 → 0.4.0

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