Merge pull request #421 from hainesr/universal-time
Fixes for the UniversalTime extra field
This commit is contained in:
		
						commit
						7bc4905a43
					
				|  | @ -4,24 +4,51 @@ module Zip | ||||||
|     HEADER_ID = 'UT' |     HEADER_ID = 'UT' | ||||||
|     register_map |     register_map | ||||||
| 
 | 
 | ||||||
|  |     ATIME_MASK = 0b010 | ||||||
|  |     CTIME_MASK = 0b100 | ||||||
|  |     MTIME_MASK = 0b001 | ||||||
|  | 
 | ||||||
|     def initialize(binstr = nil) |     def initialize(binstr = nil) | ||||||
|       @ctime = nil |       @ctime = nil | ||||||
|       @mtime = nil |       @mtime = nil | ||||||
|       @atime = nil |       @atime = nil | ||||||
|       @flag  = nil |       @flag  = 0 | ||||||
|       binstr && merge(binstr) | 
 | ||||||
|  |       merge(binstr) unless binstr.nil? | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     attr_accessor :atime, :ctime, :mtime, :flag |     attr_reader :atime, :ctime, :mtime, :flag | ||||||
|  | 
 | ||||||
|  |     def atime=(time) | ||||||
|  |       @flag = time.nil? ? @flag & ~ATIME_MASK : @flag | ATIME_MASK | ||||||
|  |       @atime = time | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def ctime=(time) | ||||||
|  |       @flag = time.nil? ? @flag & ~CTIME_MASK : @flag | CTIME_MASK | ||||||
|  |       @ctime = time | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def mtime=(time) | ||||||
|  |       @flag = time.nil? ? @flag & ~MTIME_MASK : @flag | MTIME_MASK | ||||||
|  |       @mtime = time | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|     def merge(binstr) |     def merge(binstr) | ||||||
|       return if binstr.empty? |       return if binstr.empty? | ||||||
|  | 
 | ||||||
|       size, content = initial_parse(binstr) |       size, content = initial_parse(binstr) | ||||||
|       size || return |       return if !size || size <= 0 | ||||||
|       @flag, mtime, atime, ctime = content.unpack('CVVV') | 
 | ||||||
|       mtime && @mtime ||= ::Zip::DOSTime.at(mtime) |       @flag, *times = content.unpack('Cl<l<l<') | ||||||
|       atime && @atime ||= ::Zip::DOSTime.at(atime) | 
 | ||||||
|       ctime && @ctime ||= ::Zip::DOSTime.at(ctime) |       # Parse the timestamps, in order, based on which flags are set. | ||||||
|  |       return if times[0].nil? | ||||||
|  |       @mtime ||= ::Zip::DOSTime.at(times.shift) unless @flag & MTIME_MASK == 0 | ||||||
|  |       return if times[0].nil? | ||||||
|  |       @atime ||= ::Zip::DOSTime.at(times.shift) unless @flag & ATIME_MASK == 0 | ||||||
|  |       return if times[0].nil? | ||||||
|  |       @ctime ||= ::Zip::DOSTime.at(times.shift) unless @flag & CTIME_MASK == 0 | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def ==(other) |     def ==(other) | ||||||
|  | @ -32,15 +59,15 @@ module Zip | ||||||
| 
 | 
 | ||||||
|     def pack_for_local |     def pack_for_local | ||||||
|       s = [@flag].pack('C') |       s = [@flag].pack('C') | ||||||
|       @flag & 1 != 0 && s << [@mtime.to_i].pack('V') |       s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0 | ||||||
|       @flag & 2 != 0 && s << [@atime.to_i].pack('V') |       s << [@atime.to_i].pack('l<') unless @flag & ATIME_MASK == 0 | ||||||
|       @flag & 4 != 0 && s << [@ctime.to_i].pack('V') |       s << [@ctime.to_i].pack('l<') unless @flag & CTIME_MASK == 0 | ||||||
|       s |       s | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def pack_for_c_dir |     def pack_for_c_dir | ||||||
|       s = [@flag].pack('C') |       s = [@flag].pack('C') | ||||||
|       @flag & 1 == 1 && s << [@mtime.to_i].pack('V') |       s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0 | ||||||
|       s |       s | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -0,0 +1,97 @@ | ||||||
|  | require 'test_helper' | ||||||
|  | 
 | ||||||
|  | class ZipExtraFieldUTTest < MiniTest::Test | ||||||
|  | 
 | ||||||
|  |   PARSE_TESTS = [ | ||||||
|  |     ["UT\x05\x00\x01PS>A", 0b001, true, true, false], | ||||||
|  |     ["UT\x05\x00\x02PS>A", 0b010, false, true, true], | ||||||
|  |     ["UT\x05\x00\x04PS>A", 0b100, true, false, true], | ||||||
|  |     ["UT\x09\x00\x03PS>APS>A", 0b011, false, true, false], | ||||||
|  |     ["UT\x09\x00\x05PS>APS>A", 0b101, true, false, false], | ||||||
|  |     ["UT\x09\x00\x06PS>APS>A", 0b110, false, false, true], | ||||||
|  |     ["UT\x13\x00\x07PS>APS>APS>A", 0b111, false, false, false] | ||||||
|  |   ] | ||||||
|  | 
 | ||||||
|  |   def test_parse | ||||||
|  |     PARSE_TESTS.each do |bin, flags, a, c, m| | ||||||
|  |       ut = ::Zip::ExtraField::UniversalTime.new(bin) | ||||||
|  |       assert_equal(flags, ut.flag) | ||||||
|  |       assert(ut.atime.nil? == a) | ||||||
|  |       assert(ut.ctime.nil? == c) | ||||||
|  |       assert(ut.mtime.nil? == m) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def test_parse_size_zero | ||||||
|  |     ut = ::Zip::ExtraField::UniversalTime.new("UT\x00") | ||||||
|  |     assert_equal(0b000, ut.flag) | ||||||
|  |     assert_nil(ut.atime) | ||||||
|  |     assert_nil(ut.ctime) | ||||||
|  |     assert_nil(ut.mtime) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def test_parse_size_nil | ||||||
|  |     ut = ::Zip::ExtraField::UniversalTime.new('UT') | ||||||
|  |     assert_equal(0b000, ut.flag) | ||||||
|  |     assert_nil(ut.atime) | ||||||
|  |     assert_nil(ut.ctime) | ||||||
|  |     assert_nil(ut.mtime) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def test_parse_nil | ||||||
|  |     ut = ::Zip::ExtraField::UniversalTime.new | ||||||
|  |     assert_equal(0b000, ut.flag) | ||||||
|  |     assert_nil(ut.atime) | ||||||
|  |     assert_nil(ut.ctime) | ||||||
|  |     assert_nil(ut.mtime) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def test_set_clear_times | ||||||
|  |     time = ::Zip::DOSTime.now | ||||||
|  |     ut = ::Zip::ExtraField::UniversalTime.new | ||||||
|  |     assert_equal(0b000, ut.flag) | ||||||
|  | 
 | ||||||
|  |     ut.mtime = time | ||||||
|  |     assert_equal(0b001, ut.flag) | ||||||
|  |     assert_equal(time, ut.mtime) | ||||||
|  | 
 | ||||||
|  |     ut.ctime = time | ||||||
|  |     assert_equal(0b101, ut.flag) | ||||||
|  |     assert_equal(time, ut.ctime) | ||||||
|  | 
 | ||||||
|  |     ut.atime = time | ||||||
|  |     assert_equal(0b111, ut.flag) | ||||||
|  |     assert_equal(time, ut.atime) | ||||||
|  | 
 | ||||||
|  |     ut.ctime = nil | ||||||
|  |     assert_equal(0b011, ut.flag) | ||||||
|  |     assert_nil ut.ctime | ||||||
|  | 
 | ||||||
|  |     ut.mtime = nil | ||||||
|  |     assert_equal(0b010, ut.flag) | ||||||
|  |     assert_nil ut.mtime | ||||||
|  | 
 | ||||||
|  |     ut.atime = nil | ||||||
|  |     assert_equal(0b000, ut.flag) | ||||||
|  |     assert_nil ut.atime | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def test_pack | ||||||
|  |     time = ::Zip::DOSTime.at('PS>A'.unpack1('l<')) | ||||||
|  |     ut = ::Zip::ExtraField::UniversalTime.new | ||||||
|  |     assert_equal("\x00", ut.pack_for_local) | ||||||
|  |     assert_equal("\x00", ut.pack_for_c_dir) | ||||||
|  | 
 | ||||||
|  |     ut.mtime = time | ||||||
|  |     assert_equal("\x01PS>A", ut.pack_for_local) | ||||||
|  |     assert_equal("\x01PS>A", ut.pack_for_c_dir) | ||||||
|  | 
 | ||||||
|  |     ut.atime = time | ||||||
|  |     assert_equal("\x03PS>APS>A", ut.pack_for_local) | ||||||
|  |     assert_equal("\x03PS>A", ut.pack_for_c_dir) | ||||||
|  | 
 | ||||||
|  |     ut.ctime = time | ||||||
|  |     assert_equal("\x07PS>APS>APS>A", ut.pack_for_local) | ||||||
|  |     assert_equal("\x07PS>A", ut.pack_for_c_dir) | ||||||
|  |   end | ||||||
|  | end | ||||||
		Loading…
	
		Reference in New Issue