bc3 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|