Skip to content

Commit

Permalink
Merge ee028d2 into d3027ea
Browse files Browse the repository at this point in the history
  • Loading branch information
hainesr committed Dec 15, 2019
2 parents d3027ea + ee028d2 commit eb91de9
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 12 deletions.
51 changes: 39 additions & 12 deletions lib/zip/extra_field/universal_time.rb
Expand Up @@ -4,24 +4,51 @@ class ExtraField::UniversalTime < ExtraField::Generic
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_reader :atime, :ctime, :mtime, :flag

def atime=(time)
@flag = time.nil? ? @flag & ~ATIME_MASK : @flag | ATIME_MASK
@atime = time
end

attr_accessor :atime, :ctime, :mtime, :flag
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)
Expand All @@ -32,15 +59,15 @@ def ==(other)

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
Expand Down
97 changes: 97 additions & 0 deletions test/extra_field_ut_test.rb
@@ -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

0 comments on commit eb91de9

Please sign in to comment.