bc3 0.1.0
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/BCSS_Binary_Format.txt +239 -0
- data/bin/bc3_merge.rb +155 -0
- data/examples/folder1_2011-01-21.bcss +0 -0
- data/examples/folder2_2011-01-21.bcss +0 -0
- data/examples/test_combine.rb +43 -0
- data/examples/test_filesystem.rb +6 -0
- data/examples/test_hardcoded.rb +15 -0
- data/examples/test_merge.bat +5 -0
- data/examples/test_yaml.rb +188 -0
- data/lib/bc3.rb +89 -0
- data/lib/bc3/file.rb +120 -0
- data/lib/bc3/folder.rb +239 -0
- data/lib/bc3/helper.rb +101 -0
- data/lib/bc3/parse.rb +312 -0
- data/lib/bc3/snapshot.rb +264 -0
- data/lib/bc3/time.rb +1 -0
- data/unittest/unittest_bc3.rb +66 -0
- data/unittest/unittest_bc3_file.rb +35 -0
- data/unittest/unittest_bc3_folder.rb +179 -0
- data/unittest/unittest_bc3_merge.rb +102 -0
- data/unittest/unittest_bc3_snapshot.rb +121 -0
- metadata +119 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
-------------------------------------------------------------------------------
|
2
|
+
Beyond Compare Snapshot Format Version 1.1
|
3
|
+
-------------------------------------------------------------------------------
|
4
|
+
|
5
|
+
Beyond Compare snapshots (.bcss) are binary files containing the file metadata
|
6
|
+
(names, sizes, last modified times) of a directory structure without storing
|
7
|
+
any of the file content. They are designed to be read sequentially. File
|
8
|
+
record sizes are variable, so there's no way to seek to arbitrary records
|
9
|
+
without reading all of the records before it.
|
10
|
+
|
11
|
+
|
12
|
+
===========
|
13
|
+
File Header
|
14
|
+
===========
|
15
|
+
|
16
|
+
Snapshots start with a fixed size header that contains an ID value, version
|
17
|
+
information, a creation date, and various flags, optionally followed by the
|
18
|
+
source folder's path:
|
19
|
+
|
20
|
+
- HEADER STRUCTURE -
|
21
|
+
[0..3] = 'BCSS'
|
22
|
+
[4] = Major version (UByte)
|
23
|
+
[5] = Minor version (UByte)
|
24
|
+
[6] = Minimum Supported Major Version (UByte)
|
25
|
+
[7] = Minimum Supported Minor Version (UByte)
|
26
|
+
[8..F] = Creation Time (FileTime)
|
27
|
+
[10..11] = Flags (UWord)
|
28
|
+
|
29
|
+
Bit : Meaning
|
30
|
+
0 : Compressed
|
31
|
+
1 : Source Path included
|
32
|
+
2 : Reserved
|
33
|
+
3 : UTF-8
|
34
|
+
4-15 : Reserved
|
35
|
+
|
36
|
+
[12..13] = Path Length (UWord) | Optional
|
37
|
+
[14..N] = Path (char[]) |
|
38
|
+
|
39
|
+
Version Information:
|
40
|
+
The first two version bytes represent the actual major and minor versions
|
41
|
+
of the file, and reference a specific version of this specification. The
|
42
|
+
second pair of version bytes represent the minimum snapshot version which must
|
43
|
+
be supported in order to read the snapshot file. Version 1.1 can be read by
|
44
|
+
Version 1.0 applications, so currently Major/Minor should be set to 1.1 and
|
45
|
+
Minimum should be 1.0.
|
46
|
+
|
47
|
+
Flags:
|
48
|
+
Compressed: If set everything following the header is compressed as a raw
|
49
|
+
deflate stream, as defined by RFC 1951. It is the same compression used by
|
50
|
+
.zip and .gz archives.
|
51
|
+
|
52
|
+
Source Path included: If set the original folder's path is included
|
53
|
+
immediately after the header. This is only on part of the file besides the
|
54
|
+
fixed header that is not compressed.
|
55
|
+
|
56
|
+
UTF-8: If set the snapshot was compressed on a system where the default
|
57
|
+
character encoding is UTF-8 (Linux, OS X). Filenames, paths, and link targets
|
58
|
+
will all be stored as UTF-8. If this isn't set the paths are stored using the
|
59
|
+
original OS's ANSI codepage (Windows). In that case any paths may be stored a
|
60
|
+
second time as UTF-8 in extended headers.
|
61
|
+
|
62
|
+
|
63
|
+
==========
|
64
|
+
Data Types
|
65
|
+
==========
|
66
|
+
|
67
|
+
UByte:
|
68
|
+
Unsigned 8-bit value
|
69
|
+
|
70
|
+
UInt16:
|
71
|
+
Unsigned 16-bit value
|
72
|
+
|
73
|
+
Int32:
|
74
|
+
Signed 32-bit value
|
75
|
+
|
76
|
+
UInt32:
|
77
|
+
Unsigned 32-bit value
|
78
|
+
|
79
|
+
Int64:
|
80
|
+
Signed 64-bit value
|
81
|
+
|
82
|
+
char[]:
|
83
|
+
Variable length single-byte character array (ANSI or UTF-8).
|
84
|
+
|
85
|
+
FileTime:
|
86
|
+
Windows FILETIME structure. 64-bit value representing the number of
|
87
|
+
100-nanosecond intervals since January 1, 1601 UTC. Stored in local time.
|
88
|
+
|
89
|
+
ShortString:
|
90
|
+
Variable length single-byte character string (ANSI or UTF-8). Not null
|
91
|
+
terminated.
|
92
|
+
|
93
|
+
Length : UByte
|
94
|
+
Data : char[Length]
|
95
|
+
|
96
|
+
FileExString:
|
97
|
+
Variable length single-byte character string (UTF-8). See "File Extended
|
98
|
+
Header" section for details.
|
99
|
+
|
100
|
+
|
101
|
+
===============
|
102
|
+
Content Records
|
103
|
+
===============
|
104
|
+
|
105
|
+
Immediately after the header the directory tree is stored as a series of
|
106
|
+
records. Directories are stored recursively: each one starts with the
|
107
|
+
directory header, then any files and subdirectories (and their children), then
|
108
|
+
the directory end record.
|
109
|
+
|
110
|
+
The ID_DIRECTORY record for the outer most (source) folder is not stored, so
|
111
|
+
the content stream actually starts with the first child, and continues until
|
112
|
+
it finds an unmatched ID_END_REC record. Anything following that is currently
|
113
|
+
ignored.
|
114
|
+
|
115
|
+
Each record starts with a single UByte ID value and then the data defined below.
|
116
|
+
|
117
|
+
ID_DIRECTORY (0x01)
|
118
|
+
Represents a directory on the system, or an expanded archive file.
|
119
|
+
|
120
|
+
Name : ShortString
|
121
|
+
Last Modified : FileTime
|
122
|
+
DOS Attributes : UInt32
|
123
|
+
|
124
|
+
|
125
|
+
ID_DIRECTORY_END (0xFF)
|
126
|
+
Represents the end of a directory listing. No data.
|
127
|
+
|
128
|
+
|
129
|
+
ID_FILE (0x02)
|
130
|
+
Represents a file on the system.
|
131
|
+
|
132
|
+
Name : ShortString
|
133
|
+
Last Modified : FileTime
|
134
|
+
DOS Attributes : UInt32
|
135
|
+
Size : Int32[+Int64]
|
136
|
+
If Size > 2GB, store as Int32(-1) followed by Int64
|
137
|
+
CRC32 : UInt32
|
138
|
+
|
139
|
+
|
140
|
+
ID_FILE_EX (0x03)
|
141
|
+
Represents a file on the system, with extended headers.
|
142
|
+
|
143
|
+
Name..CRC32 is the same as ID_FILE
|
144
|
+
ExtraLen : UInt16
|
145
|
+
ExtraData : Byte[ExtraLen]
|
146
|
+
|
147
|
+
|
148
|
+
ID_EXTENDED (0x04)
|
149
|
+
Extended headers
|
150
|
+
|
151
|
+
SubType : UByte
|
152
|
+
Length : UWord
|
153
|
+
Data : Byte[Length]
|
154
|
+
|
155
|
+
|
156
|
+
========================
|
157
|
+
Extended Header Subtypes
|
158
|
+
========================
|
159
|
+
|
160
|
+
Extended headers should be written in ascending numeric order. Once BC sees
|
161
|
+
an extended subtype that it doesn't undertand it stops processing ID_EXTENDED
|
162
|
+
headers until it finds one of ID_DIRECTORY/ID_DIRECTORY_END/ID_FILE/ID_FILE_EX.
|
163
|
+
|
164
|
+
|
165
|
+
EX_UTF8 (0x01)
|
166
|
+
UTF-8 encoded filename for the ID_DIRECTORY that immediately preceeded
|
167
|
+
this header. The length is given in the ID_EXTENDED header and the data is a
|
168
|
+
char[].
|
169
|
+
If the .bcss header flags indicate that the data is not UTF-8 and the
|
170
|
+
source path is included this can be included as the first record in the file
|
171
|
+
in order to give a UTF-8 version of the source path.
|
172
|
+
|
173
|
+
|
174
|
+
EX_DIRECTORY_EX (0x02)
|
175
|
+
Extended directory header for the ID_DIRECTORY that immediately preceeded
|
176
|
+
this header. Data is the record below, but Length may be larger to support
|
177
|
+
future expansion.
|
178
|
+
|
179
|
+
Flags : UByte
|
180
|
+
Bit : Meaning
|
181
|
+
0 : Error - Contents not available. Flag as a load error in BC.
|
182
|
+
|
183
|
+
|
184
|
+
EX_RESYNC (0x03)
|
185
|
+
Works around a bug in Beyond Compare's parser in versions prior to 3.2.2.
|
186
|
+
If an ID_DIRECTORY is followed by any ID_EXTENDED headers besides EX_UTF8 or
|
187
|
+
EX_DIRECTORY_EX include one copy of this header before them.
|
188
|
+
|
189
|
+
Length : UWord = 0x0001
|
190
|
+
Data : Byte[1] = 0
|
191
|
+
|
192
|
+
|
193
|
+
EX_LINK_PATH (0x04)
|
194
|
+
UTF-8 encoded symbolic link path for the ID_DIRECTORY that immediately
|
195
|
+
preceeded this header. The length is given in the ID_EXTENDED header and the
|
196
|
+
data is a char[].
|
197
|
+
|
198
|
+
|
199
|
+
=====================
|
200
|
+
File Extended Headers
|
201
|
+
=====================
|
202
|
+
|
203
|
+
Like extended headers, file extended headers should be written in ascending
|
204
|
+
numeric order.
|
205
|
+
|
206
|
+
FILE_EX_VERSION (0x01)
|
207
|
+
String representation of an executable file's Major/Minor/Maint/Build
|
208
|
+
version (e.g., "2.11.28.3542").
|
209
|
+
|
210
|
+
Length : UByte
|
211
|
+
Data : char[Length]
|
212
|
+
|
213
|
+
|
214
|
+
FILE_EX_UTF8 (0x02)
|
215
|
+
UTF-8 encoded filename. Stored as a FileExString. Only used if the UTF-8
|
216
|
+
name doesn't match the ANSI encoded one or if the filename is longer than 255
|
217
|
+
characters.
|
218
|
+
|
219
|
+
|
220
|
+
FILE_EX_LINK_PATH (0x03)
|
221
|
+
UTF-8 encoded symbolic link path. Stored as a FileExString.
|
222
|
+
|
223
|
+
|
224
|
+
FileExString
|
225
|
+
------------
|
226
|
+
Beyond Compare v2.4.1 and earlier will produce incorrect results if it
|
227
|
+
encounters a raw 0x01 byte in a file extended header. To prevent that most
|
228
|
+
strings in ID_FILE_EX extended headers are written like so:
|
229
|
+
|
230
|
+
Length : UByte[+UByte]
|
231
|
+
Data : char[Length]
|
232
|
+
|
233
|
+
If (Length <> 1) and (Length <= 127) then Length is 1 byte
|
234
|
+
Otherwise the Length is written as
|
235
|
+
Low : UByte(Length) OR 0x80
|
236
|
+
High : UByte(Length shr 7) OR 0x80
|
237
|
+
|
238
|
+
If an extended header must have a 0x01 in it (other than FILE_EX_VERSION),
|
239
|
+
increase the .bcss header's Minimum Supported Version to 1.1.
|
data/bin/bc3_merge.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
=begin rdoc
|
3
|
+
bc3_merge.rb
|
4
|
+
|
5
|
+
Merge snapshots and build a new snapshot with all files.
|
6
|
+
|
7
|
+
Example:
|
8
|
+
bc3_merge.rb '*.bcss'
|
9
|
+
Takes all snapshots in the directory and build a new one.
|
10
|
+
|
11
|
+
=Options
|
12
|
+
==Target snapshot
|
13
|
+
The filename for the target snapshot will be compound by
|
14
|
+
_snapshot_merge__ and a time stamp.
|
15
|
+
|
16
|
+
You may choose your own name:
|
17
|
+
bc3_merge.rb *.bcss -t merged_snapshot.bcss
|
18
|
+
|
19
|
+
==Folder for insertion
|
20
|
+
If you add a snapshot, you must define, how you add the snapshot.
|
21
|
+
|
22
|
+
Each snapshot has a source path, e.g. c:/user/me/folder1.
|
23
|
+
There are three different possible behaviours how you
|
24
|
+
handle the source path of your snapshoot.
|
25
|
+
|
26
|
+
Attention!
|
27
|
+
When you use wildcards inside a shell, the may be expanded.
|
28
|
+
To move the wildcards to the script, you must mask your input
|
29
|
+
|
30
|
+
Wrong:
|
31
|
+
bc3_merge.rb -r *.bcss
|
32
|
+
Correct:
|
33
|
+
bc3_merge.rb -r '*.bcss'
|
34
|
+
|
35
|
+
===Full source path
|
36
|
+
The full source path (starting from roor) will be added to the snapshot
|
37
|
+
bc3_merge.rb -r '*.bcss'
|
38
|
+
or
|
39
|
+
bc3_merge.rb -root '*.bcss'
|
40
|
+
|
41
|
+
===Add with base path
|
42
|
+
The snapshot is added with the base directory name.
|
43
|
+
|
44
|
+
With source path _c:/user/me/folder1_, the snapshot would be added as _folder1_
|
45
|
+
|
46
|
+
bc3_merge *.bcss
|
47
|
+
or
|
48
|
+
bc3_merge -b '*.bcss'
|
49
|
+
or
|
50
|
+
bc3_merge --base '*.bcss'
|
51
|
+
|
52
|
+
===Add without source path info
|
53
|
+
The snapshot is added directly to the target snapshot.
|
54
|
+
There is no source path information used.
|
55
|
+
|
56
|
+
bc3_merge -i '*.bcss'
|
57
|
+
or
|
58
|
+
bc3_merge --initial '*.bcss'
|
59
|
+
|
60
|
+
=Usage Examples
|
61
|
+
bc3_merge.rb my_folder1.bcss my_folder1.bcss
|
62
|
+
Takes the two snapshots and mix them.
|
63
|
+
|
64
|
+
bc3_merge.rb '*.bcss'
|
65
|
+
Takes all snapshots in the directory and build a new one.
|
66
|
+
=end
|
67
|
+
|
68
|
+
require 'optparse'
|
69
|
+
require 'bc3'
|
70
|
+
#~ $log.level = Log4r::DEBUG
|
71
|
+
|
72
|
+
#Collector
|
73
|
+
$options = {
|
74
|
+
target: Time.now.strftime("snapshot_merge_%Y-%m-%d_%H-%M-%S.bcss"),
|
75
|
+
source: Dir.pwd,
|
76
|
+
root: [],
|
77
|
+
base: [],
|
78
|
+
initial: [],
|
79
|
+
}
|
80
|
+
|
81
|
+
=begin
|
82
|
+
Create a Parser for command line
|
83
|
+
=end
|
84
|
+
opts = OptionParser.new()
|
85
|
+
opts.banner = "Usage: bc3_merge.rb [options] [source snapshots]" #Usage-zeile
|
86
|
+
opts.separator("Merge Beyond Compare Snapshots to a new snapshot")
|
87
|
+
|
88
|
+
=begin
|
89
|
+
Source path
|
90
|
+
=end
|
91
|
+
opts.on("-s", "--source SOURCE", "Set source path for the snapshot") { |v|
|
92
|
+
$options[:source] = v
|
93
|
+
}
|
94
|
+
=begin
|
95
|
+
Set target name.
|
96
|
+
=end
|
97
|
+
opts.on("-t", "--target TARGET", "Set target file name") { |v|
|
98
|
+
$options[:target] = v
|
99
|
+
}
|
100
|
+
|
101
|
+
=begin
|
102
|
+
Define source snapshots and how they are added.
|
103
|
+
=end
|
104
|
+
opts.on("-r", "--root SNAPSHOT_MASK", "Add snapshots with root-information") { |v|
|
105
|
+
$options[:root] << v
|
106
|
+
}
|
107
|
+
|
108
|
+
opts.on("-b", "--base SNAPSHOT_MASK", "Add snapshots with base-information") { |v|
|
109
|
+
$options[:base] << v
|
110
|
+
}
|
111
|
+
|
112
|
+
opts.on("-i", "--initial SNAPSHOT_MASK", "Add snapshots without source-information") { |v|
|
113
|
+
$options[:initial] << v
|
114
|
+
}
|
115
|
+
|
116
|
+
#Parsen der Parameter mit Exception bei ung�ltigen Parametern
|
117
|
+
begin
|
118
|
+
opts.parse!
|
119
|
+
# rescue OptionParser::MissingArgument => err
|
120
|
+
# rescue OptionParser::InvalidOption => err
|
121
|
+
rescue OptionParser::MissingArgument, OptionParser::InvalidOption => err
|
122
|
+
puts "Error:\t#{err}"
|
123
|
+
#Ausgabe der Schnittstelle
|
124
|
+
puts opts
|
125
|
+
end
|
126
|
+
|
127
|
+
snapshot = BC3::Snapshot.new( $options[:source] )
|
128
|
+
|
129
|
+
$options[:root].each{|mask|
|
130
|
+
puts "Add #{mask} in root-mode"
|
131
|
+
Dir[mask].each{|file|
|
132
|
+
snap = BC3::SnapshotParser.new(file)
|
133
|
+
snapshot << folder = BC3::Folder.new( snap.snapshot.path, snap.timestamp)
|
134
|
+
snap.snapshot.each{|key, x| folder << x }
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
($options[:base] + ARGV).each{|mask|
|
139
|
+
puts "Add #{mask} in base-mode"
|
140
|
+
Dir[mask].each{|file|
|
141
|
+
snap = BC3::SnapshotParser.new(file)
|
142
|
+
snapshot << folder = BC3::Folder.new( File.basename(snap.snapshot.path), snap.timestamp )
|
143
|
+
snap.snapshot.each{|key, x| folder << x }
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
$options[:initial].each{|mask|
|
148
|
+
puts "Add #{mask} in raw-mode"
|
149
|
+
Dir[mask].each{|file|
|
150
|
+
BC3::SnapshotParser.new(file).snapshot.each{|key, x| snapshot << x }
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
#~ puts snapshot.each.keys
|
155
|
+
snapshot.save($options[:target])
|
Binary file
|
Binary file
|
@@ -0,0 +1,43 @@
|
|
1
|
+
$:.unshift('../lib')
|
2
|
+
require 'bc3'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
test = BC3::Snapshot.newh(YAML.load(<<data
|
6
|
+
:snapshot: C:\\Temp
|
7
|
+
:content:
|
8
|
+
- :dirname: dir1
|
9
|
+
:content:
|
10
|
+
- :filename: file1.txt
|
11
|
+
:filesize: 32
|
12
|
+
data
|
13
|
+
))
|
14
|
+
|
15
|
+
test << BC3::Folder.newh(YAML.load(<<data
|
16
|
+
:dirname: dir3
|
17
|
+
:content:
|
18
|
+
- :filename: file1.txt
|
19
|
+
:filesize: 32
|
20
|
+
data
|
21
|
+
))
|
22
|
+
|
23
|
+
test << BC3::Folder.newh(YAML.load(<<data
|
24
|
+
:dirname: dir3
|
25
|
+
:content:
|
26
|
+
- :filename: file1.txt
|
27
|
+
:filesize: 32
|
28
|
+
- :filename: file2.txt
|
29
|
+
:filesize: 12
|
30
|
+
data
|
31
|
+
))
|
32
|
+
|
33
|
+
|
34
|
+
#~ puts test.to_hash.to_yaml#Hash falsch (keine Aggregation)
|
35
|
+
test.save('results/test_combine_1.bcss')#bcss aggregiert selbst
|
36
|
+
|
37
|
+
test << BC3::File.new( filename: 'dir2', filesize: 17 )
|
38
|
+
|
39
|
+
test << BC3::File.new( filename: 'test.txt', filesize: 17 )
|
40
|
+
test << BC3::File.new( filename: 'test.txt', filesize: 17 )
|
41
|
+
|
42
|
+
puts test.to_hash.to_yaml#Hash falsch (keine Aggregation)
|
43
|
+
test.save('results/test_combine_2.bcss')#bcss aggregiert selbst
|