bc3 0.1.0 → 0.1.1

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.
@@ -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