bc3 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/bc3_search.rb +1 -7
- data/changes.rd +37 -0
- data/examples/results/readme +1 -0
- data/examples/scootersoftware/UncompressedSample.bcss +0 -0
- data/examples/scootersoftware/UnicodeFilenames(Linux).bcss +0 -0
- data/examples/scootersoftware/UnicodeFilenames(Win32).bcss +0 -0
- data/examples/scootersoftware/UnicodePath(Linux).bcss +0 -0
- data/examples/scootersoftware/UnicodePath(Win32).bcss +0 -0
- data/examples/scootersoftware/readme +1 -0
- data/examples/test_reconstruct.rb +49 -0
- data/lib/bc3.rb +12 -2
- data/lib/bc3/file.rb +14 -12
- data/lib/bc3/folder.rb +36 -10
- data/lib/bc3/helper.rb +48 -1
- data/lib/bc3/parse.rb +102 -25
- data/lib/bc3/snapshot.rb +68 -13
- data/lib/bc3/time.rb +1 -1
- data/unittest/unittest_bc3_file.rb +13 -0
- data/unittest/unittest_bc3_folder.rb +4 -4
- data/unittest/unittest_bc3_helper.rb +44 -0
- data/unittest/unittest_bc3_merge.rb +2 -0
- data/unittest/unittest_bc3_snapshot.rb +10 -9
- data/unittest/unittest_bc3_time.rb +95 -0
- metadata +23 -18
- data/unittest/unittest_bc3.rb +0 -66
data/bin/bc3_search.rb
CHANGED
@@ -104,12 +104,6 @@ return the result in a yaml-list.
|
|
104
104
|
|
105
105
|
=end
|
106
106
|
|
107
|
-
=begin
|
108
|
-
fixme/test:
|
109
|
-
duplicates
|
110
|
-
save as snapshot.
|
111
|
-
=end
|
112
|
-
|
113
107
|
$testmode = false
|
114
108
|
#~ $testmode = true
|
115
109
|
if $testmode
|
@@ -123,12 +117,12 @@ puts "!!Testmode"
|
|
123
117
|
$:.unshift('../lib')
|
124
118
|
end
|
125
119
|
|
120
|
+
#~ gem 'bc3', '= 0.1.2beta'
|
126
121
|
require 'optparse'
|
127
122
|
require 'bc3'
|
128
123
|
#~ $log.level = Log4r::DEBUG
|
129
124
|
$log.level = Log4r::WARN
|
130
125
|
|
131
|
-
|
132
126
|
#Collector
|
133
127
|
$options = {
|
134
128
|
duplicates: false,
|
data/changes.rd
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
=Change History
|
2
|
+
==0.1.0: 2011-01-24 Initial version
|
3
|
+
==0.1.1: 2011-01-30
|
4
|
+
* New: BC3::Snapshot.new_filelist
|
5
|
+
* New: BC3::Folder#add_with_path
|
6
|
+
* New: bin/bc_search.rb
|
7
|
+
* Changed BC3::Snapshot.newh -> BC3::Snapshot.new_hash (newh as alias)
|
8
|
+
* Changed BC3::Snapshot.newd -> BC3::Snapshot.new_filesystem (newd as alias)
|
9
|
+
* BC3::Snapshot.save saves yaml or bcss file
|
10
|
+
* bin/bc_merge.rb can handle yaml-files
|
11
|
+
==0.1.2 2011-02-20
|
12
|
+
* BC3::SnapshotParse: Support UTF for linux files
|
13
|
+
* BC3::Snapshot#utf: Added flag. Allows creation of Linux-UTF-snapshots.
|
14
|
+
* BC3::Snapshot: Support UTF for linux files (Snapshot uses UTF-8).
|
15
|
+
* BC3::Snapshot.uncompress: Added
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
=Known problems/Todos
|
20
|
+
|
21
|
+
==Missing code
|
22
|
+
* Snapshot.to_yaml: Add UTF-texts, utf-flag
|
23
|
+
* Snapshots/Win32 with Unicode Creation error
|
24
|
+
|
25
|
+
==Misc
|
26
|
+
* No compression of snapshots (uncompression works)
|
27
|
+
* test_reconstruct.rb make little timestamp changes (accuracy of Time?)
|
28
|
+
|
29
|
+
Tests require BC3-folder comparison in background.
|
30
|
+
* Missing unittest for bcss-generation
|
31
|
+
* Missing unittest for compressed bcss.
|
32
|
+
* Missing unittest for uncompressed bcss.
|
33
|
+
|
34
|
+
==Not covered
|
35
|
+
The following features of beyond compare are not covered by this tool:
|
36
|
+
|
37
|
+
* Symbolic links
|
@@ -0,0 +1 @@
|
|
1
|
+
This folder ist the target for the examples.
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
All examples in this directory are copies from:
|
@@ -0,0 +1,49 @@
|
|
1
|
+
=begin
|
2
|
+
Take the original examples from becond compare
|
3
|
+
(http://www.scootersoftware.com/vbulletin/showthread.php?t=7210),
|
4
|
+
parse them and reconstruct them again.
|
5
|
+
|
6
|
+
=end
|
7
|
+
$:.unshift('../lib')
|
8
|
+
|
9
|
+
require 'bc3'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
def read_and_save(filename, *options )
|
13
|
+
puts "Start #{options.inspect} for #{filename}"
|
14
|
+
options.each{|opt|
|
15
|
+
puts "undefined option #{opt}" unless %w{bcss xxx yaml yaml_stdout uncompressed}.include?(opt.to_s)
|
16
|
+
}
|
17
|
+
|
18
|
+
target = File.join("results", File.basename(filename))
|
19
|
+
|
20
|
+
snap = BC3::SnapshotParser.new(filename)
|
21
|
+
|
22
|
+
puts snap.snapshot.to_hash.to_yaml if options.include?(:yaml_stdout)
|
23
|
+
snap.snapshot.save(target.sub('.bcss', '.yaml')) if options.include?(:yaml)
|
24
|
+
|
25
|
+
snap.snapshot.save(target) if options.include?(:bcss)
|
26
|
+
|
27
|
+
if options.include?(:uncompressed)
|
28
|
+
BC3::Snapshot.uncompress(filename, target.sub('.bcss', '_uncompressed.bcss'))
|
29
|
+
end
|
30
|
+
|
31
|
+
#Copy result to enable binary compare
|
32
|
+
FileUtils.copy(filename, target.sub('\.bcss', '.xxx') ) if options.include?(:xxx)
|
33
|
+
|
34
|
+
end
|
35
|
+
#ok with minor binary differences (time stamp in milliseconds-steps)
|
36
|
+
read_and_save('scootersoftware/UncompressedSample.bcss', :bcss)
|
37
|
+
|
38
|
+
#~ $log.level=Log4r::DEBUG
|
39
|
+
$log.level=Log4r::WARN
|
40
|
+
$log.level=Log4r::INFO
|
41
|
+
#~ read_and_save('scootersoftware/UnicodeFilenames(Linux).bcss', :bcss)
|
42
|
+
#~ read_and_save('scootersoftware/UnicodePath(Linux).bcss', :bcss)
|
43
|
+
|
44
|
+
=begin
|
45
|
+
2010-02-01: Will not work correct
|
46
|
+
=end
|
47
|
+
#--
|
48
|
+
#~ read_and_save('scootersoftware/UnicodeFilenames(Win32).bcss', :uncompressed, :bcss, :yaml)
|
49
|
+
#~ read_and_save('scootersoftware/UnicodePath(Win32).bcss', :bcss)
|
data/lib/bc3.rb
CHANGED
@@ -85,5 +85,15 @@ require 'bc3/time'
|
|
85
85
|
require 'bc3/parse'
|
86
86
|
|
87
87
|
module BC3
|
88
|
-
VERSION = '0.1.
|
89
|
-
end
|
88
|
+
VERSION = '0.1.2'
|
89
|
+
end
|
90
|
+
|
91
|
+
__END__
|
92
|
+
fixme logger/sublogger
|
93
|
+
|
94
|
+
Snapshot definiert logger, vererbt an subloggers dir/file
|
95
|
+
gem 'log4r', '>= 1.1.9'
|
96
|
+
require 'log4r'
|
97
|
+
log = Logger.new('log')
|
98
|
+
log2 = Logger.new('log::2::b')
|
99
|
+
|
data/lib/bc3/file.rb
CHANGED
@@ -47,12 +47,13 @@ Must contain KEYS and supports KEYS_OPTIONAL
|
|
47
47
|
end
|
48
48
|
|
49
49
|
=begin rdoc
|
50
|
-
|
50
|
+
Creates a BC3::File-object, based on data from file system.
|
51
51
|
|
52
52
|
The argument must contain:
|
53
|
-
*
|
53
|
+
* filename
|
54
|
+
* flag, if crc schould be added (default: true)
|
54
55
|
=end
|
55
|
-
def self.new_by_filename( filename )
|
56
|
+
def self.new_by_filename( filename, with_crc = true )
|
56
57
|
$log.debug("Build file #{filename} from file system")
|
57
58
|
|
58
59
|
#fixme: get attributes from file system
|
@@ -62,14 +63,15 @@ The argument must contain:
|
|
62
63
|
settings = {
|
63
64
|
filename: filename,
|
64
65
|
filesize: ::File.size(filename),
|
65
|
-
|
66
|
+
attributes: Attrib.fileattrib(filename),
|
66
67
|
#The timestamp must be 'local time' -> ignore local time zone for generation
|
67
68
|
timestamp: ::File.mtime(filename) + Time.now.gmt_offset,
|
68
69
|
}
|
69
70
|
|
70
|
-
|
71
|
+
#Binary read needed, else the crc may differ from BC3
|
72
|
+
::File.open( filename, 'rb'){|f|
|
71
73
|
settings[:crc] = Helper::crc32(f.read)
|
72
|
-
}
|
74
|
+
} if with_crc
|
73
75
|
return File.new( settings )
|
74
76
|
end
|
75
77
|
#Name of the file
|
@@ -79,6 +81,8 @@ The argument must contain:
|
|
79
81
|
attr_reader :crc
|
80
82
|
#Path (location) in snapshots
|
81
83
|
attr_reader :snapshotpath
|
84
|
+
#File attributes
|
85
|
+
attr_reader :attributes
|
82
86
|
|
83
87
|
def to_hash()
|
84
88
|
hash = {
|
@@ -118,8 +122,7 @@ ID_FILE (0x02)
|
|
118
122
|
else
|
119
123
|
bcss << 2
|
120
124
|
end
|
121
|
-
bcss << @filename
|
122
|
-
bcss << @filename
|
125
|
+
bcss << shortstring2bcss(@filename)
|
123
126
|
bcss << fixnum2int64(@timestamp.time2ad)
|
124
127
|
#DOS Attributes
|
125
128
|
bcss << @attributes #little endian storage
|
@@ -169,7 +172,7 @@ FILE_EX_LINK_PATH (0x03)
|
|
169
172
|
|
170
173
|
=end
|
171
174
|
def bcss_extended_headers()
|
172
|
-
ehead = ''
|
175
|
+
ehead = ''.force_encoding('binary')
|
173
176
|
if @version
|
174
177
|
ehead << 1
|
175
178
|
ehead << @version.size
|
@@ -187,13 +190,12 @@ FILE_EX_LINK_PATH (0x03)
|
|
187
190
|
ehead << utfsymlink.size
|
188
191
|
ehead << utfsymlink
|
189
192
|
end
|
190
|
-
#fixme
|
191
193
|
|
192
|
-
bcss = ''
|
194
|
+
bcss = ''.force_encoding('binary')
|
193
195
|
bcss << fixnum2int16(ehead.size + 2) #size including this length
|
194
196
|
bcss << ehead
|
195
197
|
|
196
|
-
bcss
|
198
|
+
bcss
|
197
199
|
end
|
198
200
|
end #File
|
199
201
|
end #BC3
|
data/lib/bc3/folder.rb
CHANGED
@@ -63,12 +63,12 @@ The argument must contain:
|
|
63
63
|
* dirname
|
64
64
|
=end
|
65
65
|
def self.new_by_dirname( dirname )
|
66
|
-
#fixme: Pattern??
|
67
|
-
#fixme: attrib
|
68
66
|
$log.debug("Build folder #{dirname} from file system")
|
69
67
|
#The timestamp must be 'local time' -> ignore local time zone for generation
|
70
68
|
folder = new( dirname, ::File.mtime(dirname) + Time.now.gmt_offset )
|
69
|
+
#fixme: attrib
|
71
70
|
Dir.chdir(dirname){
|
71
|
+
#feature idea: Define Pattern as method parameter??
|
72
72
|
Dir['*'].each{|f|
|
73
73
|
if ::File.directory?(f)
|
74
74
|
folder << Folder.new_by_dirname(f)
|
@@ -90,6 +90,10 @@ The argument must contain:
|
|
90
90
|
#timestamp
|
91
91
|
attr_reader :timestamp
|
92
92
|
attr_reader :attributes
|
93
|
+
#Part of the Extended Header Subtypes, see BC3::SnapshotParser#parse_extended_header_subtypes
|
94
|
+
attr_accessor :ex_utf
|
95
|
+
#Part of the Extended Header Subtypes, see BC3::SnapshotParser#parse_extended_header_subtypes
|
96
|
+
attr_accessor :ex_link_path
|
93
97
|
|
94
98
|
=begin rdoc
|
95
99
|
Add content to folder.
|
@@ -175,11 +179,13 @@ Options may be
|
|
175
179
|
options = [:folders, :files ] if options.empty? #take all, but not recursive
|
176
180
|
|
177
181
|
extract = {}
|
178
|
-
@content.each{|key, content|
|
182
|
+
#~ @content.each{|key, content|
|
183
|
+
#~ @content.sort_by{|key, x| x.is_a?(Folder) ? 'a' : 'b' + x.basename }.each{|key, content|
|
184
|
+
@content.sort_by{|key, x| ( x.is_a?(Folder) ? 'a%s/' : 'b%s' ) % x.basename }.each{|key, content|
|
179
185
|
case content
|
180
186
|
when File
|
181
187
|
extract[key] = content if options.include?(:files)
|
182
|
-
|
188
|
+
when Folder
|
183
189
|
extract[key+'/'] = content if options.include?(:folders)
|
184
190
|
else
|
185
191
|
raise "Internal error"
|
@@ -198,8 +204,8 @@ Options may be
|
|
198
204
|
extract
|
199
205
|
end
|
200
206
|
end
|
201
|
-
|
202
|
-
|
207
|
+
=begin rdoc
|
208
|
+
=end
|
203
209
|
def inspect()
|
204
210
|
"<BC3::Folder #{@dirname}>"
|
205
211
|
end
|
@@ -207,12 +213,14 @@ Options may be
|
|
207
213
|
Return the content in a hash.
|
208
214
|
=end
|
209
215
|
def to_hash()
|
210
|
-
#
|
216
|
+
#feature idea: Optional array with needed keys?
|
211
217
|
{
|
212
218
|
dirname: @dirname,
|
213
219
|
timestamp: @timestamp,
|
214
220
|
attributes: @attributes,
|
215
|
-
|
221
|
+
#use each to get a sorted output
|
222
|
+
#~ content: @content.values.map{|x| x.to_hash }
|
223
|
+
content: self.each.values.map{|x| x.to_hash }
|
216
224
|
}
|
217
225
|
end
|
218
226
|
=begin rdoc
|
@@ -234,8 +242,7 @@ ID_DIRECTORY_END (0xFF)
|
|
234
242
|
def bcss()
|
235
243
|
bcss = "".force_encoding('BINARY')
|
236
244
|
bcss << 1
|
237
|
-
bcss << @dirname
|
238
|
-
bcss << @dirname
|
245
|
+
bcss << shortstring2bcss(@dirname)
|
239
246
|
bcss << fixnum2int64(@timestamp.time2ad)
|
240
247
|
#DOS Attributes
|
241
248
|
bcss << @attributes
|
@@ -248,6 +255,25 @@ ID_DIRECTORY_END (0xFF)
|
|
248
255
|
}
|
249
256
|
|
250
257
|
bcss << 255
|
258
|
+
if ex_utf
|
259
|
+
$log.fatal("Untested ex_utf")
|
260
|
+
bcss << 'xxxx'
|
261
|
+
bcss << 4 #Extended Header Subtypes
|
262
|
+
=begin
|
263
|
+
EX_UTF8 (0x01)
|
264
|
+
UTF-8 encoded filename for the ID_DIRECTORY that immediately preceeded
|
265
|
+
this header. The length is given in the ID_EXTENDED header and the data is a
|
266
|
+
char[].
|
267
|
+
If the .bcss header flags indicate that the data is not UTF-8 and the
|
268
|
+
source path is included this can be included as the first record in the file
|
269
|
+
in order to give a UTF-8 version of the source path.
|
270
|
+
=end
|
271
|
+
bcss << 1
|
272
|
+
bcss << ex_utf.bytesize
|
273
|
+
#~ p ex_utf.bytesize
|
274
|
+
#~ p ex_utf
|
275
|
+
bcss << shortstring2bcss(ex_utf.dup.force_encoding('binary'))
|
276
|
+
end
|
251
277
|
bcss
|
252
278
|
end
|
253
279
|
end #Folder
|
data/lib/bc3/helper.rb
CHANGED
@@ -21,7 +21,7 @@ http://www.xxcopy.com/xxcopy06.htm
|
|
21
21
|
0 1 0 0 0 0 0 0 40h Reserved
|
22
22
|
1 0 0 0 0 0 0 0 80h Reserved
|
23
23
|
|
24
|
-
Usage
|
24
|
+
Usage
|
25
25
|
attrib = Attrib::ReadOnly | Attrib::Hidden
|
26
26
|
=end
|
27
27
|
module Attrib
|
@@ -31,6 +31,30 @@ module Attrib
|
|
31
31
|
VolumeLabel = 8
|
32
32
|
Directory = 16
|
33
33
|
Archive = 32
|
34
|
+
=begin
|
35
|
+
How to read attributs/windows:
|
36
|
+
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/68298
|
37
|
+
|
38
|
+
Easier: call attrib and analyse output
|
39
|
+
|
40
|
+
fixme: CHeck OS
|
41
|
+
=end
|
42
|
+
def self.fileattrib(filename)
|
43
|
+
attrib = 0
|
44
|
+
|
45
|
+
raise ArgumentError, "Filename #{filename} not found" unless ::File.exists?(filename)
|
46
|
+
|
47
|
+
attrib += Directory if ::File.directory?(filename)
|
48
|
+
res = `attrib #{filename}`
|
49
|
+
|
50
|
+
attrib += ReadOnly if res =~ /\A\S*R/
|
51
|
+
attrib += Hidden if res =~ /\A\S*H/
|
52
|
+
attrib += System if res =~ /\A\S*S/
|
53
|
+
#~ attrib += VolumeLabel = if res =~ /\A\S*/
|
54
|
+
attrib += Archive if res =~ /\A\S*A/
|
55
|
+
|
56
|
+
attrib
|
57
|
+
end
|
34
58
|
end
|
35
59
|
|
36
60
|
=begin rdoc
|
@@ -81,6 +105,7 @@ AD's epoch (Bignum) as an bit sequence.
|
|
81
105
|
('%064b' % int).scan(/(\d{8})/).flatten.reverse.each{|b|
|
82
106
|
bindata << b.to_i(2)
|
83
107
|
}
|
108
|
+
raise ArgumentError unless $~.post_match.empty?
|
84
109
|
raise ArgumentError unless bindata.size == 8 #int was too big
|
85
110
|
bindata
|
86
111
|
end
|
@@ -93,6 +118,7 @@ Same as Helper#fixnum2int64, but as 4 bytes string.
|
|
93
118
|
('%032b' % int).scan(/(\d{8})/).flatten.reverse.each{|b|
|
94
119
|
bindata << b.to_i(2)
|
95
120
|
}
|
121
|
+
raise ArgumentError unless $~.post_match.empty?
|
96
122
|
raise ArgumentError unless bindata.size == 4 #int was too big
|
97
123
|
bindata
|
98
124
|
end
|
@@ -105,9 +131,30 @@ Same as Helper#fixnum2int64, but as 2 bytes string.
|
|
105
131
|
('%016b' % int).scan(/(\d{8})/).flatten.reverse.each{|b|
|
106
132
|
bindata << b.to_i(2)
|
107
133
|
}
|
134
|
+
raise ArgumentError unless $~.post_match.empty?
|
108
135
|
raise ArgumentError unless bindata.size == 2 #int was too big
|
109
136
|
bindata
|
110
137
|
end
|
111
138
|
|
139
|
+
=begin rdoc
|
140
|
+
Save a shortstring to bcss
|
141
|
+
|
142
|
+
ShortString:
|
143
|
+
Variable length single-byte character string (ANSI or UTF-8).
|
144
|
+
Not null terminated.
|
145
|
+
|
146
|
+
UTF-8: If set the snapshot was compressed on a system where the default
|
147
|
+
character encoding is UTF-8 (Linux, OS X). Filenames, paths, and link targets
|
148
|
+
will all be stored as UTF-8. If this isn't set the paths are stored using the
|
149
|
+
original OS's ANSI codepage (Windows). In that case any paths may be stored a
|
150
|
+
second time as UTF-8 in extended headers.
|
151
|
+
|
152
|
+
=end
|
153
|
+
def shortstring2bcss(shortstring)
|
154
|
+
bcss = "".force_encoding('BINARY')
|
155
|
+
bcss << shortstring.bytesize
|
156
|
+
bcss << shortstring.dup.force_encoding('binary')
|
157
|
+
bcss
|
158
|
+
end
|
112
159
|
end #Helper
|
113
160
|
end #BC3
|
data/lib/bc3/parse.rb
CHANGED
@@ -3,10 +3,9 @@ Parser for BC3-Snapshots.
|
|
3
3
|
|
4
4
|
The snapshot may be compressed or uncompressed.
|
5
5
|
|
6
|
-
|
6
|
+
Test see examples/test_reconstruct.
|
7
|
+
|
7
8
|
=end
|
8
|
-
$:.unshift('..') if $0 == __FILE__ #only test
|
9
|
-
require 'bc3'
|
10
9
|
|
11
10
|
require "zlib"
|
12
11
|
module BC3
|
@@ -67,19 +66,20 @@ Parser for a given bcss-file.
|
|
67
66
|
@compressed = rawdata[16].getbyte(0) & 1 != 0
|
68
67
|
@sourcepath = rawdata[16].getbyte(0) & 2 != 0
|
69
68
|
@reserved = rawdata[16].getbyte(0) & 4 != 0
|
70
|
-
@utf = rawdata[16].getbyte(0) & 8 != 0
|
69
|
+
@utf = rawdata[16].getbyte(0) & 8 != 0
|
70
|
+
@log.warn("UTF-data") if @utf
|
71
71
|
if rawdata[17] != "\x0"
|
72
72
|
@log.warn("2nd flag byte is filled")
|
73
73
|
end
|
74
74
|
|
75
75
|
#Analyse Source path
|
76
|
-
|
77
76
|
#Delete second length parameter for source path
|
78
77
|
if rawdata.slice!(19) != "\x0"
|
79
78
|
@log.warn("Path > 255 not supported")
|
80
79
|
raise "Path > 255 not supported"
|
81
80
|
end
|
82
81
|
path, body = parse_shortstring(rawdata[18..-1])
|
82
|
+
path.force_encoding('utf-8') if @utf
|
83
83
|
if @compressed
|
84
84
|
=begin
|
85
85
|
Flags:
|
@@ -101,9 +101,10 @@ Code from http://www.ruby-forum.com/topic/136825
|
|
101
101
|
end
|
102
102
|
|
103
103
|
@snapshot = Snapshot.new(path, @timestamp)
|
104
|
-
|
104
|
+
@snapshot.utf = @utf
|
105
|
+
|
105
106
|
parse_body(body)
|
106
|
-
end #
|
107
|
+
end #read_bcss
|
107
108
|
#Snapshot-object, result of the parsing.
|
108
109
|
attr_reader :snapshot
|
109
110
|
attr_reader :timestamp
|
@@ -193,6 +194,16 @@ ID_FILE_EX (0x03)
|
|
193
194
|
}.merge(extradata)
|
194
195
|
)
|
195
196
|
=begin
|
197
|
+
ID_EXTENDED (0x04)
|
198
|
+
Extended headers
|
199
|
+
|
200
|
+
SubType : UByte
|
201
|
+
Length : UWord
|
202
|
+
Data : Byte[Length]
|
203
|
+
=end
|
204
|
+
when "\x04" #file
|
205
|
+
parse_extended_header_subtypes(body, folderstack.last)
|
206
|
+
=begin
|
196
207
|
ID_DIRECTORY_END (0xFF)
|
197
208
|
Represents the end of a directory listing. No data.
|
198
209
|
=end
|
@@ -205,8 +216,7 @@ ID_DIRECTORY_END (0xFF)
|
|
205
216
|
end
|
206
217
|
end
|
207
218
|
if folderstack.size > 1
|
208
|
-
@log.error("Folders in Folderstack not closed correct")
|
209
|
-
p folderstack.size
|
219
|
+
@log.error("Folders in Folderstack not closed correct - #{folderstack.size} levels open")
|
210
220
|
end
|
211
221
|
end
|
212
222
|
=begin rdoc
|
@@ -216,12 +226,17 @@ Get a "shortstring".
|
|
216
226
|
|
217
227
|
Return shortstring and rest of string.
|
218
228
|
=end
|
219
|
-
def parse_shortstring( string )
|
229
|
+
def parse_shortstring( string )
|
220
230
|
#Get length of path
|
221
231
|
pathsize = string.slice!(0).bytes.first
|
222
232
|
# + rawdata[19].bytes.first * 255 #--test it
|
223
|
-
|
233
|
+
if @utf
|
234
|
+
return [string.slice!(0,pathsize).force_encoding('utf-8'), string]
|
235
|
+
else
|
236
|
+
return [string.slice!(0,pathsize), string]
|
237
|
+
end
|
224
238
|
end
|
239
|
+
|
225
240
|
=begin rdoc
|
226
241
|
Get a "longstring".
|
227
242
|
|
@@ -236,6 +251,11 @@ Return longstring and rest of string.
|
|
236
251
|
@log.warn("longstring > 255 not supported")
|
237
252
|
raise "longstring > 255 not supported"
|
238
253
|
end
|
254
|
+
if @utf
|
255
|
+
return [string.slice!(0,stringsize).force_encoding('utf-8'), string]
|
256
|
+
else
|
257
|
+
return [string.slice!(0,stringsize), string]
|
258
|
+
end
|
239
259
|
return [string.slice!(0,stringsize), string]
|
240
260
|
end
|
241
261
|
=begin rdoc
|
@@ -300,7 +320,7 @@ FILE_EX_LINK_PATH (0x03)
|
|
300
320
|
#loop on extradata...
|
301
321
|
while !extradata_string.empty?
|
302
322
|
case flag = extradata_string.slice!(0)
|
303
|
-
when "\x01"
|
323
|
+
when "\x01" #version
|
304
324
|
extradata[:version], extradata_string = parse_shortstring( extradata_string )
|
305
325
|
when "\x02" #UTF-8 encoded filename
|
306
326
|
extradata[:utfpath], extradata_string = parse_shortstring( extradata_string )
|
@@ -319,19 +339,76 @@ FILE_EX_LINK_PATH (0x03)
|
|
319
339
|
#~ end
|
320
340
|
extradata
|
321
341
|
end
|
342
|
+
=begin rdoc
|
343
|
+
========================
|
344
|
+
Extended Header Subtypes
|
345
|
+
========================
|
346
|
+
|
347
|
+
Extended headers should be written in ascending numeric order. Once BC sees
|
348
|
+
an extended subtype that it doesn't undertand it stops processing ID_EXTENDED
|
349
|
+
headers until it finds one of ID_DIRECTORY/ID_DIRECTORY_END/ID_FILE/ID_FILE_EX.
|
350
|
+
|
351
|
+
Side effect: the parameter will be shortened.
|
352
|
+
=end
|
353
|
+
def parse_extended_header_subtypes(data, last_folder)
|
354
|
+
continue = true
|
355
|
+
while continue
|
356
|
+
case flag = data.slice!(0)
|
357
|
+
=begin
|
358
|
+
EX_UTF8 (0x01)
|
359
|
+
UTF-8 encoded filename for the ID_DIRECTORY that immediately preceeded
|
360
|
+
this header. The length is given in the ID_EXTENDED header and the data is a
|
361
|
+
char[].
|
362
|
+
If the .bcss header flags indicate that the data is not UTF-8 and the
|
363
|
+
source path is included this can be included as the first record in the file
|
364
|
+
in order to give a UTF-8 version of the source path.
|
365
|
+
=end
|
366
|
+
when "\x01"
|
367
|
+
ex_utf, data = parse_shortstring( data )
|
368
|
+
last_folder.ex_utf = ex_utf.force_encoding('utf-8')
|
369
|
+
=begin
|
370
|
+
EX_DIRECTORY_EX (0x02)
|
371
|
+
Extended directory header for the ID_DIRECTORY that immediately preceeded
|
372
|
+
this header. Data is the record below, but Length may be larger to support
|
373
|
+
future expansion.
|
374
|
+
|
375
|
+
Flags : UByte
|
376
|
+
Bit : Meaning
|
377
|
+
0 : Error - Contents not available. Flag as a load error in BC.
|
378
|
+
=end
|
379
|
+
when "\x02" #UTF-8 encoded filename
|
380
|
+
@log.fatal("Undefined extended_header_subtypes 2")
|
381
|
+
ex_directory_ex = data.slice!(0)
|
382
|
+
=begin
|
383
|
+
EX_RESYNC (0x03)
|
384
|
+
Works around a bug in Beyond Compare's parser in versions prior to 3.2.2.
|
385
|
+
If an ID_DIRECTORY is followed by any ID_EXTENDED headers besides EX_UTF8 or
|
386
|
+
EX_DIRECTORY_EX include one copy of this header before them.
|
387
|
+
|
388
|
+
Length : UWord = 0x0001
|
389
|
+
Data : Byte[1] = 0
|
390
|
+
=end
|
391
|
+
when "\x03" #UTF-8 encoded symbolic link path
|
392
|
+
@log.fatal("Undefined extended_header_subtypes 3")
|
393
|
+
size = data.slice!(0).bytes.first
|
394
|
+
ex_resync = data.slice!(size)
|
395
|
+
=begin
|
396
|
+
EX_LINK_PATH (0x04)
|
397
|
+
UTF-8 encoded symbolic link path for the ID_DIRECTORY that immediately
|
398
|
+
preceeded this header. The length is given in the ID_EXTENDED header and the
|
399
|
+
data is a char[].
|
400
|
+
=end
|
401
|
+
when "\x04" #UTF-8 encoded symbolic link path
|
402
|
+
@log.fatal("Undefined extended_header_subtypes 4")
|
403
|
+
ex_link_path, data = parse_shortstring( data )
|
404
|
+
last_folder.ex_link_path = ex_link_path.force_encoding('utf-8')
|
405
|
+
else
|
406
|
+
@log.debug("Stop extended header subtype handling #{flag.inspect} <#{data.inspect}>")
|
407
|
+
continue = false #stop evaluation
|
408
|
+
end
|
409
|
+
end #while continue
|
410
|
+
end #parse_extended_header_subtypes
|
411
|
+
|
322
412
|
end #SnapshotParser
|
323
413
|
def to_hash; @snapshot.to_hash; end
|
324
414
|
end
|
325
|
-
|
326
|
-
if $0 == __FILE__
|
327
|
-
require 'yaml'
|
328
|
-
#~ x = BC3::SnapshotParser.new('../../examples/results/testdir_2011-01-16.bcssx' )
|
329
|
-
#~ x = BC3::SnapshotParser.new('../../examples/results/bc3_2011-01-16.bcss' )
|
330
|
-
x = BC3::SnapshotParser.new('../../Uncompressed Sample/Uncompressed Sample.bcss' )
|
331
|
-
#~ puts x.snapshot.to_hash.to_yaml
|
332
|
-
x.snapshot.save('../../Uncompressed Sample/Uncompressed Sample_reconstructed.bcss')
|
333
|
-
FileUtils.copy(
|
334
|
-
'../../Uncompressed Sample/Uncompressed Sample_reconstructed.bcss',
|
335
|
-
'../../Uncompressed Sample/Uncompressed Sample_reconstructed.xxxx'
|
336
|
-
)
|
337
|
-
end
|
data/lib/bc3/snapshot.rb
CHANGED
@@ -49,6 +49,8 @@ A snapsot-hash must contain:
|
|
49
49
|
raise ArgumentError, "Content is no array" unless data[:content].is_a?(Array)
|
50
50
|
|
51
51
|
snapshot = new( data[:snapshot], data[:timestamp] )
|
52
|
+
snapshot.utf = data[:utf]
|
53
|
+
|
52
54
|
data[:content].each{| element |
|
53
55
|
if element.has_key?(:dirname)
|
54
56
|
snapshot << Folder.newh(element)
|
@@ -114,18 +116,67 @@ Empty folders end with a slash.
|
|
114
116
|
snap
|
115
117
|
end #self.new_filelist
|
116
118
|
|
117
|
-
#Keep old definition
|
119
|
+
#Keep old definition, version 0.1.0
|
118
120
|
class << self
|
119
121
|
alias :newd :new_filesystem
|
120
122
|
alias :newh :new_hash
|
121
123
|
end
|
122
124
|
|
125
|
+
=begin rdoc
|
126
|
+
Uncompress a snapshot and save it again.
|
127
|
+
=end
|
128
|
+
def self.uncompress(filename, filename2 = nil)
|
129
|
+
rawdata = nil
|
130
|
+
::File.open( filename, 'rb' ){|f| rawdata = f.read() }
|
131
|
+
|
132
|
+
compressed = rawdata[16].getbyte(0) & 1 != 0
|
133
|
+
|
134
|
+
pathlength = rawdata.slice(18).bytes.first
|
135
|
+
if rawdata.slice(19) != "\x0"
|
136
|
+
@log.warn("Path > 255 not supported")
|
137
|
+
raise "Path > 255 not supported"
|
138
|
+
end
|
139
|
+
path = rawdata[20,pathlength]
|
140
|
+
|
141
|
+
header = rawdata[0,16]
|
142
|
+
if compressed
|
143
|
+
header << (rawdata.slice(16).bytes.first - 1)
|
144
|
+
else
|
145
|
+
header << rawdata.slice(16)
|
146
|
+
end
|
147
|
+
header << rawdata[17,3] #keep original length
|
148
|
+
#~ header << path.bytesize #byte 18
|
149
|
+
#~ header << 0 #byte 19 - for long pathes
|
150
|
+
header << path
|
151
|
+
|
152
|
+
#will change again for compressed data
|
153
|
+
body = rawdata[20 + pathlength ..-1]
|
154
|
+
if compressed
|
155
|
+
begin
|
156
|
+
body= Zlib::Inflate.inflate(body); #Unclear problem
|
157
|
+
rescue Zlib::DataError
|
158
|
+
$log.debug("Zlib::DataError occured - try with raw deflate")
|
159
|
+
#no luck with Zlib decompression. Let's try with raw deflate,
|
160
|
+
#like some broken browsers do.
|
161
|
+
body= Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(body)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
::File.open( filename2, 'wb' ){|f|
|
166
|
+
f << header
|
167
|
+
f << body
|
168
|
+
} if filename2
|
169
|
+
header+body
|
170
|
+
end
|
171
|
+
|
123
172
|
#homepath of the snapshot
|
124
173
|
attr_reader :path
|
125
174
|
#Content of the snapshot
|
126
175
|
attr_reader :basefolder
|
127
176
|
#Time stamp from snapshot. Default 'now'
|
128
177
|
attr_reader :timestamp
|
178
|
+
#UTF-Flag. Is set by SnapshotParser#read_bcss
|
179
|
+
attr_accessor :utf
|
129
180
|
|
130
181
|
=begin rdoc
|
131
182
|
Add content (folders/files) to snapshot.
|
@@ -265,11 +316,14 @@ Usefull in combination with yaml:
|
|
265
316
|
puts snapshot.to_hash.to_yaml
|
266
317
|
=end
|
267
318
|
def to_hash()
|
268
|
-
{
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
319
|
+
result = {
|
320
|
+
snapshot: @path,
|
321
|
+
timestamp: @timestamp,
|
322
|
+
content: @basefolder.each.values.map{| x | x.to_hash }
|
323
|
+
}
|
324
|
+
result[:utf] = @utf unless @utf.nil?
|
325
|
+
|
326
|
+
result
|
273
327
|
end
|
274
328
|
=begin rdoc
|
275
329
|
Prepare a snapshot (bcss-file).
|
@@ -380,24 +434,25 @@ second time as UTF-8 in extended headers.
|
|
380
434
|
|
381
435
|
# [10..11] = Flags (UWord)
|
382
436
|
|
383
|
-
#~ Bit : Meaning
|
384
|
-
#~ 0 : Compressed
|
385
|
-
#~ 1 : Source Path included
|
386
|
-
#~ 2 : Reserved
|
387
|
-
#~ 3 : UTF-8
|
437
|
+
#~ Bit/Value : Meaning
|
438
|
+
#~ 0/1 : Compressed
|
439
|
+
#~ 1/2 : Source Path included
|
440
|
+
#~ 2/4 : Reserved
|
441
|
+
#~ 3/8 : UTF-8
|
388
442
|
#~ 4-15 : Reserved
|
389
443
|
flag = 0 #no flag set
|
390
444
|
flag += 2 #Source Path included
|
391
445
|
flag += 1 if compressed
|
446
|
+
flag += 8 if @utf
|
392
447
|
header << flag
|
393
448
|
header << 0
|
394
449
|
|
395
450
|
# [12..13] = Path Length (UWord) | Optional
|
396
|
-
header << @path.
|
451
|
+
header << @path.bytesize
|
397
452
|
header << 0 #fixme if path > 255
|
398
453
|
raise "too long path" if @path.size > 155 #fixme
|
399
454
|
# [14..N] = Path (char[]) |
|
400
|
-
header << @path
|
455
|
+
header << @path.dup.force_encoding('binary')
|
401
456
|
|
402
457
|
header
|
403
458
|
end #header
|
data/lib/bc3/time.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
=begin rdoc
|
2
1
|
bignum too big to convert into `unsigned long' (RangeError)
|
3
2
|
AD_EPOCH = 116_444_736_000_000_000
|
4
3
|
AD_MULTIPLIER = 10_000_000
|
5
4
|
# convert a Time object to AD's epoch
|
6
5
|
def time2ad()
|
7
6
|
(self.to_i * AD_MULTIPLIER) + AD_EPOCH
|
8
7
|
end
|
9
8
|
# convert from AD's time string to a Time object
|
10
9
|
def self.ad2time(time)
|
11
10
|
Time.at((time.to_i - AD_EPOCH) / AD_MULTIPLIER)
|
12
11
|
end
|
12
|
+
=begin rdoc
|
13
13
|
bignum too big to convert into `unsigned long' (RangeError)
|
14
14
|
AD_EPOCH = 116_444_736_000_000_000
|
15
15
|
AD_MULTIPLIER = 10_000_000
|
16
16
|
# convert a Time object to AD's epoch
|
17
17
|
def time2ad()
|
18
18
|
(self.to_f * AD_MULTIPLIER) + AD_EPOCH
|
19
19
|
end
|
20
20
|
# convert from AD's time string to a Time object
|
21
21
|
def self.ad2time(time)
|
22
22
|
Time.at((time.to_f - AD_EPOCH) / AD_MULTIPLIER)
|
23
23
|
end
|
@@ -30,6 +30,19 @@ class Test_file < Test::Unit::TestCase
|
|
30
30
|
assert_equal('x', file.basename )
|
31
31
|
assert_equal(file.filename, file.basename )
|
32
32
|
end
|
33
|
+
def test_new_by_filename()
|
34
|
+
file = BC3::File.new_by_filename( __FILE__)
|
35
|
+
assert_equal(__FILE__, file.filename )
|
36
|
+
assert_equal(__FILE__, file.basename )
|
37
|
+
assert_equal(file.filename, file.basename )
|
38
|
+
assert_kind_of(Integer, file.crc )
|
39
|
+
##Sure?
|
40
|
+
assert_equal(BC3::Attrib::Archive, file.attributes )
|
41
|
+
|
42
|
+
#Test without crc
|
43
|
+
file = BC3::File.new_by_filename( __FILE__, false)
|
44
|
+
assert_equal(nil, file.crc )
|
45
|
+
end
|
33
46
|
def test_hash()
|
34
47
|
assert_equal( { filename: 'f', filesize: 5, timestamp: $timestamp, attributes: BC3::Attrib::Archive, crc: nil},
|
35
48
|
BC3::File.new( filename: 'f', filesize: 5, timestamp: $timestamp ).to_hash
|
@@ -192,14 +192,14 @@ class Test_folder < Test::Unit::TestCase
|
|
192
192
|
assert_equal( %w{dir2/test.txt},folder.each(:recursive,:files).keys )
|
193
193
|
|
194
194
|
folder.add_with_path('dir2/subdir', BC3::File.new(filename: 'test.txt', filesize: 5 ))
|
195
|
-
assert_equal( %w{dir2/test.txt dir2/
|
195
|
+
assert_equal( %w{dir2/subdir/test.txt dir2/test.txt},folder.each(:recursive,:files).keys )
|
196
196
|
|
197
197
|
folder.add_with_path('dir4/subdir', BC3::File.new(filename: 'test.txt', filesize: 5 ))
|
198
|
-
assert_equal( %w{dir2/test.txt dir2/
|
198
|
+
assert_equal( %w{dir2/subdir/test.txt dir2/test.txt dir4/subdir/test.txt},folder.each(:recursive,:files).keys )
|
199
199
|
|
200
|
-
#Added at end
|
200
|
+
#Added at end, but each is sorted.
|
201
201
|
folder.add_with_path('dir0', BC3::File.new(filename: 'test.txt', filesize: 5 ))
|
202
|
-
assert_equal( %w{
|
202
|
+
assert_equal( %w{dir0/test.txt dir2/subdir/test.txt dir2/test.txt dir4/subdir/test.txt},folder.each(:recursive,:files).keys )
|
203
203
|
end
|
204
204
|
end
|
205
205
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
gem 'test-unit'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
$:.unshift('../lib')
|
5
|
+
require 'bc3/helper'
|
6
|
+
|
7
|
+
class Test_helper < Test::Unit::TestCase
|
8
|
+
include BC3::Helper
|
9
|
+
def test_fixnum2int64()
|
10
|
+
assert_equal("\x05\x00\x00\x00\x00\x00\x00\x00", fixnum2int64(5))
|
11
|
+
assert_equal("\xff\x00\x00\x00\x00\x00\x00\x00", fixnum2int64(255))
|
12
|
+
assert_equal("\x00\x01\x00\x00\x00\x00\x00\x00", fixnum2int64(256))
|
13
|
+
assert_equal("\x01\x01\x00\x00\x00\x00\x00\x00", fixnum2int64(257))
|
14
|
+
assert_equal("\x00\x04\x00\x00\x00\x00\x00\x00", fixnum2int64(1024))
|
15
|
+
assert_equal("\xff\xff\xff\xff\xff\xff\xff\xff", fixnum2int64( 'ffffffffffffffff'.to_i(16)))
|
16
|
+
|
17
|
+
assert_raise(ArgumentError){fixnum2int64('ffffffffffffffff'.to_i(16) + 1)}
|
18
|
+
end
|
19
|
+
def test_fixnum2int32()
|
20
|
+
assert_equal("\x05\x00\x00\x00", fixnum2int32(5))
|
21
|
+
assert_equal("\xff\x00\x00\x00", fixnum2int32(255))
|
22
|
+
assert_equal("\x00\x01\x00\x00", fixnum2int32(256))
|
23
|
+
assert_equal("\x01\x01\x00\x00", fixnum2int32(257))
|
24
|
+
assert_equal("\x00\x04\x00\x00", fixnum2int32(1024))
|
25
|
+
assert_equal("\xff\xff\xff\xff", fixnum2int32( 'ffffffff'.to_i(16)))
|
26
|
+
|
27
|
+
assert_raise(ArgumentError){fixnum2int32('ffffffff'.to_i(16) + 1)}
|
28
|
+
end
|
29
|
+
def test_fixnum2int16()
|
30
|
+
assert_equal("\x05\x00", fixnum2int16(5))
|
31
|
+
assert_equal("\xff\x00", fixnum2int16(255))
|
32
|
+
assert_equal("\x00\x01", fixnum2int16(256))
|
33
|
+
assert_equal("\x01\x01", fixnum2int16(257))
|
34
|
+
assert_equal("\x00\x04", fixnum2int16(1024))
|
35
|
+
assert_equal("\xff\xff", fixnum2int16( 'ffff'.to_i(16)))
|
36
|
+
|
37
|
+
assert_raise(ArgumentError){fixnum2int16('ffff'.to_i(16) + 1)}
|
38
|
+
end
|
39
|
+
def test_crc32()
|
40
|
+
assert_equal('CBF43926', '%X' % BC3::Helper.crc32('123456789'))
|
41
|
+
assert_equal('D87F7E0C', '%X' % BC3::Helper.crc32('test'))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -81,6 +81,7 @@ class Test_merge < Test::Unit::TestCase
|
|
81
81
|
merge = {
|
82
82
|
snapshot: 'merge_b',
|
83
83
|
timestamp: result[:timestamp],
|
84
|
+
utf: false,
|
84
85
|
content: [
|
85
86
|
{ dirname: 'snap1', timestamp: $timestamp, attributes: 16, content: Snap1[:content] },
|
86
87
|
{ dirname: 'snap2', timestamp: $timestamp, attributes: 16, content: Snap2[:content] },
|
@@ -98,6 +99,7 @@ class Test_merge < Test::Unit::TestCase
|
|
98
99
|
merge = {
|
99
100
|
snapshot: 'merge_i',
|
100
101
|
timestamp: result[:timestamp],
|
102
|
+
utf: false,
|
101
103
|
content: Snap1[:content] + Snap2[:content]
|
102
104
|
}
|
103
105
|
assert_equal(merge, result)
|
@@ -64,8 +64,9 @@ class Test_snapshot < Test::Unit::TestCase
|
|
64
64
|
|
65
65
|
hash[:content] << { filename: 'file2', filesize: 15, timestamp: $timestamp, crc: nil, attributes: BC3::Attrib::Archive, }
|
66
66
|
assert_equal(hash, BC3::Snapshot.new_hash( hash ).to_hash)
|
67
|
-
|
68
|
-
|
67
|
+
|
68
|
+
#No <<, sort must be seen
|
69
|
+
hash[:content].unshift({ dirname: 'dir1', timestamp: $timestamp, :content => [], attributes: BC3::Attrib::Directory, })
|
69
70
|
assert_equal(hash, BC3::Snapshot.new_hash( hash ).to_hash)
|
70
71
|
end
|
71
72
|
def test_new_filesystem()
|
@@ -104,17 +105,17 @@ class Test_snapshot < Test::Unit::TestCase
|
|
104
105
|
)
|
105
106
|
|
106
107
|
assert_equal( %w{
|
107
|
-
file
|
108
108
|
folder/
|
109
|
-
folder/file1
|
110
109
|
folder/subfolder/
|
111
110
|
folder/subfolder/subfile1
|
112
111
|
folder/subfolder2/
|
112
|
+
folder/file1
|
113
|
+
file
|
113
114
|
}, bc3.each(:files, :folders ).keys)
|
114
115
|
|
115
116
|
assert_equal( %w{
|
116
|
-
file
|
117
117
|
folder/
|
118
|
+
file
|
118
119
|
}, bc3.each(:rootonly, :files, :folders ).keys)
|
119
120
|
|
120
121
|
assert_equal( %w{
|
@@ -124,9 +125,9 @@ class Test_snapshot < Test::Unit::TestCase
|
|
124
125
|
}, bc3.each(:folders ).keys)
|
125
126
|
|
126
127
|
assert_equal( %w{
|
127
|
-
file
|
128
|
-
folder/file1
|
129
128
|
folder/subfolder/subfile1
|
129
|
+
folder/file1
|
130
|
+
file
|
130
131
|
}, bc3.each(:files ).keys)
|
131
132
|
|
132
133
|
|
@@ -220,12 +221,12 @@ class Test_snapshot < Test::Unit::TestCase
|
|
220
221
|
|
221
222
|
#test index
|
222
223
|
assert_equal(%w{
|
223
|
-
file
|
224
224
|
folder/
|
225
|
-
folder/file1
|
226
225
|
folder/subfolder/
|
227
226
|
folder/subfolder/subfile1
|
228
227
|
folder/subfolder2/
|
228
|
+
folder/file1
|
229
|
+
file
|
229
230
|
}, bc3.index.keys)
|
230
231
|
end #test_index
|
231
232
|
def test_index_automatic_reset()
|
@@ -0,0 +1,95 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
There is a problem with nanoseconds.
|
3
|
+
-> test_conversion_nanoseconds and test_conversion_now are inactivated.
|
4
|
+
=end
|
5
|
+
gem 'test-unit'
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
$:.unshift('../lib')
|
9
|
+
require 'bc3/time'
|
10
|
+
|
11
|
+
class Test_time < Test::Unit::TestCase
|
12
|
+
#Nonoseconds are ignored (missing feature in Time-class?)
|
13
|
+
def test_ad2time()
|
14
|
+
assert_equal('2006-08-17 09:19:04 000000000',
|
15
|
+
Time.ad2time(128002727440000000).strftime("%Y-%m-%d %H:%M:%S %9N")
|
16
|
+
)
|
17
|
+
assert_equal('2006-08-17 09:18:20 000000000',
|
18
|
+
Time.ad2time(128002727000000000).strftime("%Y-%m-%d %H:%M:%S %9N")
|
19
|
+
)
|
20
|
+
|
21
|
+
assert_match(/2006-08-17 09:19:04 08082\d\d\d\d/,
|
22
|
+
Time.ad2time(128002727440808261).strftime("%Y-%m-%d %H:%M:%S %9N")
|
23
|
+
)
|
24
|
+
#~ assert_equal('2006-08-17 09:19:04 0808261',
|
25
|
+
assert_equal('2006-08-17 09:19:04 080825567', #??
|
26
|
+
Time.ad2time(128002727440808261).strftime("%Y-%m-%d %H:%M:%S %9N")
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_time2ad()
|
31
|
+
assert_equal(128002464000000000, Time.utc(2006,8,17).time2ad())
|
32
|
+
assert_equal(128002727440000000, Time.local(2006,8,17,9,19,4).time2ad())
|
33
|
+
|
34
|
+
#Test does not work. Split in two areas (high/Low) makes calculating unpossible.
|
35
|
+
#~ p 60 * 60 * 1_000_000_000 #one hour in nanoseconds
|
36
|
+
#~ assert_equal( 3_600_000_000_000, #one hour in nanoseconds
|
37
|
+
#~ Time.utc(2006,8,17,12).time2ad() - Time.utc(2006,8,17,11).time2ad()
|
38
|
+
#~ )
|
39
|
+
|
40
|
+
#~ assert_equal( 3_600_000_000_000, #one hour in nanoseconds
|
41
|
+
#~ Time.utc(2006,8,17).time2ad() - Time.local(2006,8,17).time2ad()
|
42
|
+
#~ )
|
43
|
+
end
|
44
|
+
def test_conversions()
|
45
|
+
testtime = Time.utc(2006,8,17)
|
46
|
+
assert_equal(testtime, Time.ad2time(testtime.time2ad))
|
47
|
+
|
48
|
+
testtime = Time.local(2006,8,17)
|
49
|
+
assert_equal(testtime, Time.ad2time(testtime.time2ad))
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_conversion_ok()
|
54
|
+
|
55
|
+
#last 7 digits must be 0.
|
56
|
+
last_logon = 129393178970000000
|
57
|
+
|
58
|
+
time = Time.ad2time(last_logon)
|
59
|
+
last_logon2 = time.time2ad()
|
60
|
+
time2 = Time.ad2time(last_logon2)
|
61
|
+
|
62
|
+
assert_equal(time, time2)
|
63
|
+
|
64
|
+
assert_equal(0, last_logon - last_logon2)
|
65
|
+
assert_equal(last_logon, last_logon2)
|
66
|
+
end
|
67
|
+
def test_conversion_nanoseconds()
|
68
|
+
|
69
|
+
#last 7 digits are nano seconds.
|
70
|
+
last_logon = 129393178978310001
|
71
|
+
time = Time.ad2time(last_logon)
|
72
|
+
|
73
|
+
last_logon2 = time.time2ad()
|
74
|
+
time2 = Time.ad2time(last_logon2)
|
75
|
+
|
76
|
+
#~ assert_in_delta(0, time - time2, 0.001)
|
77
|
+
assert_equal(0, time - time2)
|
78
|
+
assert_equal(time, time2)
|
79
|
+
|
80
|
+
assert_equal(0, last_logon - last_logon2)
|
81
|
+
assert_equal(last_logon, last_logon2)
|
82
|
+
|
83
|
+
end
|
84
|
+
#
|
85
|
+
def test_conversion_now()
|
86
|
+
testtime = Time.now
|
87
|
+
#problem:
|
88
|
+
#AD-time is "the number of 100-nanosecond intervals since ..."
|
89
|
+
#Time.now is nanoseconds
|
90
|
+
assert_in_delta(0, testtime - Time.ad2time(testtime.time2ad), 1E-6)
|
91
|
+
#~ assert_equal(0, testtime - Time.ad2time(testtime.time2ad))
|
92
|
+
#~ assert_equal(testtime, Time.ad2time(testtime.time2ad))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bc3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Knut Lickert
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-02-20 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -47,9 +47,11 @@ extensions: []
|
|
47
47
|
|
48
48
|
extra_rdoc_files:
|
49
49
|
- BCSS_Binary_Format.txt
|
50
|
+
- changes.rd
|
50
51
|
- bin/bc3_merge.rb
|
51
52
|
- bin/bc3_search.rb
|
52
53
|
files:
|
54
|
+
- changes.rd
|
53
55
|
- BCSS_Binary_Format.txt
|
54
56
|
- bin/bc3_merge.rb
|
55
57
|
- bin/bc3_search.rb
|
@@ -60,19 +62,28 @@ files:
|
|
60
62
|
- lib/bc3/parse.rb
|
61
63
|
- lib/bc3/snapshot.rb
|
62
64
|
- lib/bc3/time.rb
|
63
|
-
- unittest/unittest_bc3.rb
|
64
|
-
- unittest/unittest_bc3_file.rb
|
65
|
-
- unittest/unittest_bc3_folder.rb
|
66
|
-
- unittest/unittest_bc3_snapshot.rb
|
67
|
-
- unittest/unittest_bc3_merge.rb
|
68
|
-
- unittest/unittest_bc3_search.rb
|
69
65
|
- examples/test_combine.rb
|
70
66
|
- examples/test_filesystem.rb
|
71
67
|
- examples/test_hardcoded.rb
|
72
68
|
- examples/test_yaml.rb
|
69
|
+
- examples/test_reconstruct.rb
|
73
70
|
- examples/test_merge.bat
|
74
71
|
- examples/folder1_2011-01-21.bcss
|
75
72
|
- examples/folder2_2011-01-21.bcss
|
73
|
+
- examples/results/readme
|
74
|
+
- examples/scootersoftware/readme
|
75
|
+
- examples/scootersoftware/UncompressedSample.bcss
|
76
|
+
- examples/scootersoftware/UnicodeFilenames(Linux).bcss
|
77
|
+
- examples/scootersoftware/UnicodePath(Linux).bcss
|
78
|
+
- examples/scootersoftware/UnicodeFilenames(Win32).bcss
|
79
|
+
- examples/scootersoftware/UnicodePath(Win32).bcss
|
80
|
+
- unittest/unittest_bc3_helper.rb
|
81
|
+
- unittest/unittest_bc3_time.rb
|
82
|
+
- unittest/unittest_bc3_file.rb
|
83
|
+
- unittest/unittest_bc3_folder.rb
|
84
|
+
- unittest/unittest_bc3_snapshot.rb
|
85
|
+
- unittest/unittest_bc3_merge.rb
|
86
|
+
- unittest/unittest_bc3_search.rb
|
76
87
|
has_rdoc: true
|
77
88
|
homepage: http://gems.rubypla.net/bc3
|
78
89
|
licenses: []
|
@@ -109,16 +120,10 @@ signing_key:
|
|
109
120
|
specification_version: 3
|
110
121
|
summary: Build and analyse Beyond Compare (BC3) Snapshot-Files.
|
111
122
|
test_files:
|
112
|
-
- unittest/
|
123
|
+
- unittest/unittest_bc3_helper.rb
|
124
|
+
- unittest/unittest_bc3_time.rb
|
113
125
|
- unittest/unittest_bc3_file.rb
|
114
126
|
- unittest/unittest_bc3_folder.rb
|
115
127
|
- unittest/unittest_bc3_snapshot.rb
|
116
128
|
- unittest/unittest_bc3_merge.rb
|
117
129
|
- unittest/unittest_bc3_search.rb
|
118
|
-
- examples/test_combine.rb
|
119
|
-
- examples/test_filesystem.rb
|
120
|
-
- examples/test_hardcoded.rb
|
121
|
-
- examples/test_yaml.rb
|
122
|
-
- examples/test_merge.bat
|
123
|
-
- examples/folder1_2011-01-21.bcss
|
124
|
-
- examples/folder2_2011-01-21.bcss
|
data/unittest/unittest_bc3.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
gem 'test-unit'
|
2
|
-
require 'test/unit'
|
3
|
-
|
4
|
-
$:.unshift('../lib')
|
5
|
-
require 'bc3'
|
6
|
-
|
7
|
-
class Test_helper < Test::Unit::TestCase
|
8
|
-
include BC3::Helper
|
9
|
-
def test_fixnum2int64()
|
10
|
-
assert_equal("\x05\x00\x00\x00\x00\x00\x00\x00", fixnum2int64(5))
|
11
|
-
assert_equal("\xff\x00\x00\x00\x00\x00\x00\x00", fixnum2int64(255))
|
12
|
-
assert_equal("\x00\x01\x00\x00\x00\x00\x00\x00", fixnum2int64(256))
|
13
|
-
assert_equal("\x01\x01\x00\x00\x00\x00\x00\x00", fixnum2int64(257))
|
14
|
-
assert_equal("\x00\x04\x00\x00\x00\x00\x00\x00", fixnum2int64(1024))
|
15
|
-
end
|
16
|
-
def test_fixnum2int32()
|
17
|
-
assert_equal("\x05\x00\x00\x00", fixnum2int32(5))
|
18
|
-
assert_equal("\xff\x00\x00\x00", fixnum2int32(255))
|
19
|
-
assert_equal("\x00\x01\x00\x00", fixnum2int32(256))
|
20
|
-
assert_equal("\x01\x01\x00\x00", fixnum2int32(257))
|
21
|
-
assert_equal("\x00\x04\x00\x00", fixnum2int32(1024))
|
22
|
-
end
|
23
|
-
def test_crc32()
|
24
|
-
assert_equal('CBF43926', '%X' % BC3::Helper.crc32('123456789'))
|
25
|
-
assert_equal('D87F7E0C', '%X' % BC3::Helper.crc32('test'))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
class Test_time < Test::Unit::TestCase
|
31
|
-
#Nonoseconds are ignored (missing feature in Time-class?)
|
32
|
-
def test_ad2time()
|
33
|
-
assert_equal('2006-08-17 09:19:04 000000000',
|
34
|
-
Time.ad2time(128002727440808261).strftime("%Y-%m-%d %H:%M:%S %9N")
|
35
|
-
)
|
36
|
-
assert_equal('2006-08-17 09:19:04 000000000',
|
37
|
-
Time.ad2time(128002727440000000).strftime("%Y-%m-%d %H:%M:%S %9N")
|
38
|
-
)
|
39
|
-
end
|
40
|
-
def test_time2ad()
|
41
|
-
assert_equal(128002464000000000, Time.utc(2006,8,17).time2ad())
|
42
|
-
assert_equal(128002727440000000, Time.local(2006,8,17,9,19,4).time2ad())
|
43
|
-
|
44
|
-
#Test does not work. Split in two areas (high/Low) makes calculating unpossible.
|
45
|
-
#~ p 60 * 60 * 1_000_000_000 #one hour in nanoseconds
|
46
|
-
#~ assert_equal( 3_600_000_000_000, #one hour in nanoseconds
|
47
|
-
#~ Time.utc(2006,8,17,12).time2ad() - Time.utc(2006,8,17,11).time2ad()
|
48
|
-
#~ )
|
49
|
-
|
50
|
-
#~ assert_equal( 3_600_000_000_000, #one hour in nanoseconds
|
51
|
-
#~ Time.utc(2006,8,17).time2ad() - Time.local(2006,8,17).time2ad()
|
52
|
-
#~ )
|
53
|
-
end
|
54
|
-
def test_conversions()
|
55
|
-
testtime = Time.utc(2006,8,17)
|
56
|
-
assert_equal(testtime, Time.ad2time(testtime.time2ad))
|
57
|
-
|
58
|
-
testtime = Time.local(2006,8,17)
|
59
|
-
assert_equal(testtime, Time.ad2time(testtime.time2ad))
|
60
|
-
|
61
|
-
#Test with now does not work. Changed nanoseconds?
|
62
|
-
#~ testtime = Time.now
|
63
|
-
#~ assert_equal(testtime, Time.ad2time(testtime.time2ad))
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|