bc3 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/BCSS_Binary_Format.txt +46 -39
- data/bin/bc3_merge.rb +7 -4
- data/bin/bc3_search.rb +342 -0
- data/lib/bc3.rb +5 -5
- data/lib/bc3/file.rb +87 -8
- data/lib/bc3/folder.rb +93 -44
- data/lib/bc3/helper.rb +12 -0
- data/lib/bc3/parse.rb +50 -25
- data/lib/bc3/snapshot.rb +241 -13
- data/unittest/unittest_bc3_file.rb +6 -0
- data/unittest/unittest_bc3_folder.rb +32 -6
- data/unittest/unittest_bc3_merge.rb +4 -1
- data/unittest/unittest_bc3_search.rb +231 -0
- data/unittest/unittest_bc3_snapshot.rb +218 -30
- metadata +11 -6
data/BCSS_Binary_Format.txt
CHANGED
@@ -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
|
data/bin/bc3_merge.rb
CHANGED
@@ -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
|
|
data/bin/bc3_search.rb
ADDED
@@ -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
|