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' | ||||
|     register_map | ||||
| 
 | ||||
|     ATIME_MASK = 0b010 | ||||
|     CTIME_MASK = 0b100 | ||||
|     MTIME_MASK = 0b001 | ||||
| 
 | ||||
|     def initialize(binstr = nil) | ||||
|       @ctime = nil | ||||
|       @mtime = nil | ||||
|       @atime = nil | ||||
|       @flag  = nil | ||||
|       binstr && merge(binstr) | ||||
|       @flag  = 0 | ||||
| 
 | ||||
|       merge(binstr) unless binstr.nil? | ||||
|     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) | ||||
|       return if binstr.empty? | ||||
| 
 | ||||
|       size, content = initial_parse(binstr) | ||||
|       size || return | ||||
|       @flag, mtime, atime, ctime = content.unpack('CVVV') | ||||
|       mtime && @mtime ||= ::Zip::DOSTime.at(mtime) | ||||
|       atime && @atime ||= ::Zip::DOSTime.at(atime) | ||||
|       ctime && @ctime ||= ::Zip::DOSTime.at(ctime) | ||||
|       return if !size || size <= 0 | ||||
| 
 | ||||
|       @flag, *times = content.unpack('Cl<l<l<') | ||||
| 
 | ||||
|       # 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 | ||||
| 
 | ||||
|     def ==(other) | ||||
|  | @ -32,15 +59,15 @@ module Zip | |||
| 
 | ||||
|     def pack_for_local | ||||
|       s = [@flag].pack('C') | ||||
|       @flag & 1 != 0 && s << [@mtime.to_i].pack('V') | ||||
|       @flag & 2 != 0 && s << [@atime.to_i].pack('V') | ||||
|       @flag & 4 != 0 && s << [@ctime.to_i].pack('V') | ||||
|       s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0 | ||||
|       s << [@atime.to_i].pack('l<') unless @flag & ATIME_MASK == 0 | ||||
|       s << [@ctime.to_i].pack('l<') unless @flag & CTIME_MASK == 0 | ||||
|       s | ||||
|     end | ||||
| 
 | ||||
|     def pack_for_c_dir | ||||
|       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 | ||||
|     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