bc3 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
1
  -------------------------------------------------------------------------------
2
2
  Beyond Compare Snapshot Format Version 1.1
3
+ January 25, 2011
4
+ Copyright (c) 2001-2011 Scooter Software, Inc.
5
+ This document may be redistributed.
6
+ For newer versions contact support@scootersoftware.com
3
7
  -------------------------------------------------------------------------------
4
8
 
5
9
  Beyond Compare snapshots (.bcss) are binary files containing the file metadata
@@ -9,6 +13,46 @@ record sizes are variable, so there's no way to seek to arbitrary records
9
13
  without reading all of the records before it.
10
14
 
11
15
 
16
+ ==========
17
+ Data Types
18
+ ==========
19
+
20
+ All integer values are stored in little-endian format.
21
+
22
+ UByte:
23
+ Unsigned 8-bit value
24
+
25
+ UInt16:
26
+ Unsigned 16-bit value
27
+
28
+ Int32:
29
+ Signed 32-bit value
30
+
31
+ UInt32:
32
+ Unsigned 32-bit value
33
+
34
+ Int64:
35
+ Signed 64-bit value
36
+
37
+ char[]:
38
+ Variable length single-byte character array (ANSI or UTF-8).
39
+
40
+ FileTime:
41
+ Windows FILETIME structure. 64-bit value representing the number of
42
+ 100-nanosecond intervals since January 1, 1601 UTC. Stored in local time.
43
+
44
+ ShortString:
45
+ Variable length single-byte character string (ANSI or UTF-8). Not null
46
+ terminated.
47
+
48
+ Length : UByte
49
+ Data : char[Length]
50
+
51
+ FileExString:
52
+ Variable length single-byte character string (UTF-8). See "File Extended
53
+ Header" section for details.
54
+
55
+
12
56
  ===========
13
57
  File Header
14
58
  ===========
@@ -60,44 +104,6 @@ original OS's ANSI codepage (Windows). In that case any paths may be stored a
60
104
  second time as UTF-8 in extended headers.
61
105
 
62
106
 
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
107
  ===============
102
108
  Content Records
103
109
  ===============
@@ -201,7 +207,8 @@ File Extended Headers
201
207
  =====================
202
208
 
203
209
  Like extended headers, file extended headers should be written in ascending
204
- numeric order.
210
+ numeric order. Multiple headers may occur within a single ID_FILE_EX record,
211
+ and compliant parsers should break once they read a type they don't recognize.
205
212
 
206
213
  FILE_EX_VERSION (0x01)
207
214
  String representation of an executable file's Major/Minor/Maint/Build
@@ -94,7 +94,7 @@ opts.on("-s", "--source SOURCE", "Set source path for the snapshot") { |v|
94
94
  =begin
95
95
  Set target name.
96
96
  =end
97
- opts.on("-t", "--target TARGET", "Set target file name") { |v|
97
+ opts.on("-t", "--target TARGET", "Set target file name (must be .yaml or .bcss)") { |v|
98
98
  $options[:target] = v
99
99
  }
100
100
 
@@ -113,6 +113,8 @@ opts.on("-i", "--initial SNAPSHOT_MASK", "Add snapshots without source-informati
113
113
  $options[:initial] << v
114
114
  }
115
115
 
116
+
117
+ $log.info("Start bc_merge #{BC3::VERSION}")
116
118
  #Parsen der Parameter mit Exception bei ung�ltigen Parametern
117
119
  begin
118
120
  opts.parse!
@@ -122,6 +124,7 @@ rescue OptionParser::MissingArgument, OptionParser::InvalidOption => err
122
124
  puts "Error:\t#{err}"
123
125
  #Ausgabe der Schnittstelle
124
126
  puts opts
127
+ exit
125
128
  end
126
129
 
127
130
  snapshot = BC3::Snapshot.new( $options[:source] )
@@ -131,7 +134,7 @@ $options[:root].each{|mask|
131
134
  Dir[mask].each{|file|
132
135
  snap = BC3::SnapshotParser.new(file)
133
136
  snapshot << folder = BC3::Folder.new( snap.snapshot.path, snap.timestamp)
134
- snap.snapshot.each{|key, x| folder << x }
137
+ snap.snapshot.each(:folders, :files, :rootonly ){|key, x| folder << x }
135
138
  }
136
139
  }
137
140
 
@@ -140,14 +143,14 @@ $options[:root].each{|mask|
140
143
  Dir[mask].each{|file|
141
144
  snap = BC3::SnapshotParser.new(file)
142
145
  snapshot << folder = BC3::Folder.new( File.basename(snap.snapshot.path), snap.timestamp )
143
- snap.snapshot.each{|key, x| folder << x }
146
+ snap.snapshot.each(:folders, :files, :rootonly ){|key, x| folder << x }
144
147
  }
145
148
  }
146
149
 
147
150
  $options[:initial].each{|mask|
148
151
  puts "Add #{mask} in raw-mode"
149
152
  Dir[mask].each{|file|
150
- BC3::SnapshotParser.new(file).snapshot.each{|key, x| snapshot << x }
153
+ BC3::SnapshotParser.new(file).snapshot.each(:folders, :files, :rootonly ){|key, x| snapshot << x }
151
154
  }
152
155
  }
153
156
 
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env ruby
2
+ =begin rdoc
3
+ Search inside bc3-snapshots.
4
+
5
+ Please remember:
6
+ BC3 offers itself a search function.
7
+
8
+ This bc3_merge may be usefull, if you want to search in
9
+ many snapshots for specific files or directories.
10
+
11
+ Usage:
12
+ bc3_search.rb 'pattern' *.bcss [options]
13
+
14
+ The ' around $pattern are needed, else the shell may expand the pattern as shell glob.
15
+
16
+ Search is only possible on basenames (no pathes).
17
+
18
+ ==Pattern
19
+ The search pattern handles the following parameters:
20
+ * * Matches any character
21
+ * ? Matches exactly one character
22
+ * [] and {} may make problems.
23
+
24
+ The pattern is translated to a regular expression.
25
+ With option -r the pattern is used as a regular expression,
26
+ no conversion is made.
27
+
28
+ ==Report the Result
29
+ ===YAML-File
30
+ Use option -y
31
+
32
+ ===BC3-Snapshot
33
+ Use option -b (or --bcss) and the search result is
34
+ saved as a snapshot.
35
+ bc_search -b extract *.bcss
36
+ Creates snapshots with the search result.
37
+ The Filename is the original name with an additional 'extract'".
38
+
39
+ bc_search *.bcss -b
40
+ Filename will get a "_extract_YYYY-MM-DD-hhmm".
41
+ The option -b must be at the end (else the *.bcss will be taken to build a new filename)
42
+
43
+
44
+ Use option -B (or --BCSS) and the global search result is
45
+ saved as a snapshot.
46
+ bc_search -B extract.bcss *.bcss
47
+ Creates a snapshot 'extract.bcss' with all collected search results.
48
+
49
+ bc_search *.bcss -B
50
+ Filename will get a "bc3_search_YYYY-MM-DD-hhmm.bcss".
51
+ The option -B must be at the end (else the *.bcss will be taken to build a new filename)
52
+
53
+ ==Duplicates
54
+ With option -d (or --duplicates) you can restrict the
55
+ results on duplicates inside a snapshot.
56
+
57
+ With option -D (or --Duplicates) you can restrict the
58
+ results on duplicates in the snapshots.
59
+ Only files with a duplicate in another (or the same) snapshot
60
+ are listed.
61
+
62
+ The duplicate search can be combined with yaml or bcss-option.
63
+
64
+ ===BC3 method
65
+ Beyond Compare offers itself a way to find duplicates:
66
+ <em>
67
+ The only way to find duplicate files is to flatten your folder structure so that all the files appear to be at the same level. You then can enable/show the CRC column, sort by it, then manually scan the file list for files with the same size and CRC code. I wouldn't advise it for a large number of files and folders, though. It could take a while to calculate all the CRC values, and it would be a tedious process to manually look through them all for duplicates.
68
+ </em>
69
+ http://www.scootersoftware.com/vbulletin/showpost.php?p=27736&postcount=4
70
+
71
+
72
+ ==Examples:
73
+ bc3_search.rb '*.rbw' *.bcss
74
+ Search for rbw-files in all snapshots.
75
+
76
+ ===duplicate option
77
+ bc3_search.rb -d '*' *.bcss
78
+ bc3_search.rb --duplicates '*' *.bcss
79
+ Search for duplicates inside all snapshots.
80
+ Use the pattern '*' to check all files.
81
+
82
+ bc3_search.rb -d '*.rb' *.bcss
83
+ Search for duplicate ruby scripts in all snapshots.
84
+
85
+
86
+ bc3_search.rb -D '*' *.bcss
87
+ bc3_search.rb --Duplicates '*' *.bcss
88
+ Search for duplicates inside snapshots.
89
+ Each snapshot lists all files, where you can find a duplicate
90
+ in the same or another selected snapshot.
91
+
92
+
93
+ ===regexp option
94
+ bc3_search.rb -r '[ab].*\\.rbw' *.bcss
95
+ bc3_search.rb --regexp '[ab].*\\.rbw' *.bcss
96
+ Search for rbw-files starting with an a or a b.
97
+ The option -r switch to regular expression.
98
+
99
+ ===yaml option
100
+ bc3_search.rb '*.rbw' *.bcss -y
101
+ bc3_search.rb '*.rbw' *.bcss -yaml
102
+ Search for rbw-files in all snapshots and
103
+ return the result in a yaml-list.
104
+
105
+ =end
106
+
107
+ =begin
108
+ fixme/test:
109
+ duplicates
110
+ save as snapshot.
111
+ =end
112
+
113
+ $testmode = false
114
+ #~ $testmode = true
115
+ if $testmode
116
+ puts "!!Testmode"
117
+ #~ ARGV = %w{* ../unittest/test_search.bcss}
118
+ #~ ARGV = %w{-r .* ../unittest/test_search.bcss}
119
+ #~ ARGV = %w{-y * ../unittest/test_search.bcss}
120
+ #~ ARGV = %w{-d * ../unittest/test_search.bcss}
121
+ #~ ARGV = %w{-B * ../unittest/test_search.bcss}
122
+ #~ ARGV = %w{-d * ../unittest/test_search.bcss -b}
123
+ $:.unshift('../lib')
124
+ end
125
+
126
+ require 'optparse'
127
+ require 'bc3'
128
+ #~ $log.level = Log4r::DEBUG
129
+ $log.level = Log4r::WARN
130
+
131
+
132
+ #Collector
133
+ $options = {
134
+ duplicates: false,
135
+ yaml: false,
136
+ }
137
+
138
+ =begin
139
+ Create a Parser for command line
140
+ =end
141
+ opts = OptionParser.new()
142
+ opts.banner = "Usage: bc3_search.rb pattern snapshots [options]" #Usage-zeile
143
+ opts.separator("Search inside Beyond Compare Snapshots")
144
+
145
+ =begin
146
+ Pattern is a regular expression.
147
+ =end
148
+ opts.on('-r', "--[no-]regexp", "Use a ruby-regular expression") { |v|
149
+ $log.debug("Set usage of regular expression")
150
+ $options[:regexp] = v
151
+ }
152
+
153
+ =begin
154
+ Define search for duplicates
155
+ =end
156
+ opts.on("-d", "--[no-]duplicates", "Restrict on duplicates inside snapshot") { |v|
157
+ $options[:duplicates] = v
158
+ }
159
+ opts.on("-D", "--Duplicates", "Search duplicates in all snapshots") { |v|
160
+ $options[:Duplicates] = true
161
+ }
162
+
163
+
164
+ =begin
165
+ Puts result in yaml-style
166
+ =end
167
+ opts.on("-y", "--[no-]yaml", "Give result in yaml-style") { |v|
168
+ $options[:yaml] = v
169
+ }
170
+
171
+ =begin
172
+ Save result as bcss-file
173
+ =end
174
+ opts.on("-b", "--bcss [FILENAME_ADDITION]", "Save result as bcss-file") { |v|
175
+ $options[:bcss] = v ? v : "extract_#{Time.now.strftime('%Y-%m-%d_%H%M')}"
176
+ }
177
+ opts.on("-B", "--BCSS [FILENAME]", "Save result as bcss-file") { |v|
178
+ $options[:BCSS] = v ? v : "bc3_search_#{Time.now.strftime('%Y-%m-%d_%H%M')}.bcss"
179
+ }
180
+
181
+
182
+
183
+ $log.info("Start bc_search #{BC3::VERSION}")
184
+ #Parsen der Parameter mit Exception bei ung�ltigen Parametern
185
+ begin
186
+ opts.parse!
187
+ # rescue OptionParser::MissingArgument => err
188
+ # rescue OptionParser::InvalidOption => err
189
+ rescue OptionParser::MissingArgument, OptionParser::InvalidOption => err
190
+ puts "Error:\t#{err}"
191
+ #Ausgabe der Schnittstelle
192
+ puts opts
193
+ exit
194
+ end
195
+
196
+ if ARGV.empty?
197
+ puts "No search pattern and no Snapshot found"
198
+ exit
199
+ end
200
+
201
+ #Build regexp from pattern
202
+ pattern = ARGV.shift.dup #ARGV-parameter are frozen -> dup
203
+ if ! $options[:regexp]
204
+ pattern.gsub!(/\./, '\.')
205
+ pattern.gsub!(/\*/, '.*')
206
+ pattern.gsub!(/\?/, '.')
207
+ end
208
+ begin
209
+ searchpattern = Regexp.new(pattern)
210
+ rescue RegexpError
211
+ puts "Argument Error - Searchpattern has error"
212
+ exit
213
+ end
214
+
215
+ puts "Start bc3_search for pattern <#{searchpattern.source}>"
216
+ if ARGV.empty?
217
+ puts "No Snapshot found"
218
+ end
219
+
220
+ if $options[:BCSS] and File.exist?($options[:BCSS])
221
+ puts "Target Snapshot #{$options[:BCSS]} already exists"
222
+ exit
223
+ end
224
+
225
+ puts "Restrict on global duplicates" if $options[:Duplicates]
226
+
227
+ =begin
228
+ Loop on non-option arguments and create snapshots.
229
+
230
+ With option -D (or --Duplicates) a glogal crc-overview is generated.
231
+ =end
232
+ $snapshots = {}
233
+ crc_list = {}
234
+ ARGV.each{|filename|
235
+ if ! File.exist?(filename)
236
+ puts "Snapshot #{filename} not found"
237
+ next
238
+ end
239
+ $snapshots[filename] = BC3::SnapshotParser.new(filename).snapshot
240
+ if $options[:Duplicates]
241
+ $snapshots[filename].each(:files){|path, file|
242
+ ( crc_list[file.crc] ||= [] ) << path
243
+ }
244
+ end
245
+ }
246
+
247
+ $global_duplicates = []
248
+ crc_list.each{| crc, filelist| $global_duplicates.concat(filelist) if filelist.size > 1 }
249
+
250
+ #Collector for global hits
251
+ $bcss = BC3::Snapshot.new( Time.now.strftime('bc3_search_%Y-%m-%d_%H%M.bcss'))
252
+
253
+
254
+ =begin
255
+ Loop on snapshots
256
+ =end
257
+ $snapshots.each{|file, snapshot|
258
+ puts "Analyse #{file} (#{snapshot.path})"
259
+ count = Hash.new(0)
260
+ bcss = BC3::Snapshot.new( snapshot.path + '_extract' )
261
+
262
+ =begin
263
+ Prepare duplicate search if needed.
264
+ =end
265
+ if $options[:duplicates]
266
+ snapshot.build_index
267
+ crc_list = snapshot.statistic['duplicates_by_crc']
268
+
269
+ duplicates = []
270
+ if crc_list != 0
271
+ crc_list.each{| crc, filelist| duplicates.concat(filelist) }
272
+ else
273
+ puts "No duplicates found"
274
+ end
275
+ end
276
+ =begin
277
+ Loop on snapshot content
278
+ =end
279
+ folders = []
280
+ snapshot.each(:recursive, :files, :folders){|key, element|
281
+ next unless File.basename(key) =~ searchpattern
282
+ next if $options[:duplicates] and ! duplicates.include?(key)
283
+ next if $options[:Duplicates] and ! $global_duplicates.include?(key)
284
+
285
+ #Copy folder, but without content (content is no hit, only the folder itself).
286
+ if element.kind_of?(BC3::Folder)
287
+ element = BC3::Folder.new( element.dirname, element.timestamp, element.attributes )
288
+ folders << key #separated collection of folders.
289
+ end
290
+ #Add to result
291
+ if key =~ /\/./
292
+ $bcss.basefolder.add_with_path(File.dirname(key), element)
293
+ bcss.basefolder.add_with_path(File.dirname(key), element)
294
+ else
295
+ $bcss << element
296
+ bcss << element
297
+ end
298
+
299
+ } #snapshot
300
+
301
+ if $options[:yaml]
302
+ puts bcss.to_hash.to_yaml
303
+ else
304
+ folders.each{|key|
305
+ puts "\t#{key}"
306
+ }
307
+ bcss.each{|key,element| #only files
308
+ puts "\t#{key}"
309
+ }
310
+ {
311
+ :file => bcss.statistic['files'],
312
+ :folder => folders.size,
313
+ }.each{|key, value|
314
+ next if value == 0
315
+ puts "Found %3i %s%s in %s" % [ value, key, value > 1 ? 's' : '', file ]
316
+ }
317
+ end
318
+
319
+ =begin
320
+ Create extract as snapshot.
321
+ =end
322
+ if $options[:bcss]
323
+ filename = File.basename(file, '.bcss') + "_#{$options[:bcss]}.bcss"
324
+ if File.exist?(filename)
325
+ puts "Target Snapshot #{filename} already exists"
326
+ next
327
+ end
328
+
329
+ puts "Save result in #{filename}"
330
+ bcss.save(filename)
331
+ end
332
+
333
+ puts '' #space for next snapshot
334
+ } #snapshots
335
+
336
+ =begin
337
+ Create extract as snapshot.
338
+ =end
339
+ if $options[:BCSS]
340
+ puts "Save result in #{$options[:BCSS]}"
341
+ $bcss.save($options[:BCSS])
342
+ end