bc3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,6 @@
1
+ $:.unshift('../lib')
2
+ #Define directroy to be packed.
3
+ dir = 'folder1'
4
+ x = BC3::Snapshot.newd(dir)
5
+ x.save("results/#{dir}_compressed.bcss", true)
6
+ x.save("results/#{dir}_compressed.xxxx", true)