bc3 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,15 @@
|
|
1
|
+
$:.unshift('../lib')
|
2
|
+
require 'bc3'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
def test_hard_coded()
|
6
|
+
test = BC3::Snapshot.new('C:\Program Files (x86)\Beyond Compare 3')
|
7
|
+
test << folder = BC3::Folder.new('Helpers', Time.local(2011,1,10,14,5,37))
|
8
|
+
folder << folder2 = BC3::Folder.new('HtmlTidy', Time.local(2010,12,27,12,8,10))
|
9
|
+
folder2 << BC3::File.new(filename: 'HtmlTidy.exe', filesize: 111592, timestamp: Time.local(2007,8,16,19,0,45))
|
10
|
+
folder << BC3::File.new(filename: 'MediaInfo.bat', filesize: 32, timestamp: Time.local(2010,9,3,9,10,45))
|
11
|
+
test << BC3::File.new(filename: '7zxa.dll', filesize: 162816, timestamp: Time.local(2008,6,17,11,4,14))
|
12
|
+
|
13
|
+
test.save('results/test_hardcoded.xxx')
|
14
|
+
test.save('results/test_hardcoded.bcss')
|
15
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
@echo off
|
2
|
+
ruby -I ../lib ../bin/bc3_merge.rb -t results/test_merge.bcss -s testsnap -r 'folder*.bcss'
|
3
|
+
REM ~ ruby -I ../lib ../bin/bc3_merge.rb -t results/test_merge.bcss -s testsnap -b 'folder*.bcss'
|
4
|
+
REM ~ ruby -I ../lib ../bin/bc3_merge.rb -t results/test_merge.bcss -s testsnap -i 'folder*.bcss'
|
5
|
+
|
@@ -0,0 +1,188 @@
|
|
1
|
+
$:.unshift('../lib')
|
2
|
+
require 'bc3'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
extract = <<data
|
6
|
+
:snapshot: C:\\Program Files (x86)\\Beyond Compare 3
|
7
|
+
:timestamp: 2011-01-12 15:58:17 +00:00
|
8
|
+
:content:
|
9
|
+
#~ - :dirname: Helpers
|
10
|
+
#~ :timestamp: 2011-01-10 14:05:37 +00:00
|
11
|
+
#~ :attributes: 16
|
12
|
+
#~ :content:
|
13
|
+
#~ - :dirname: HtmlTidy
|
14
|
+
#~ :timestamp: 2010-12-27 12:08:10 +00:00
|
15
|
+
#~ :content:
|
16
|
+
#~ - :filename: HtmlTidy.exe
|
17
|
+
#~ :filesize: 110592
|
18
|
+
#~ :crc:
|
19
|
+
#~ :timestamp: 2007-08-16 19:00:00 +00:00
|
20
|
+
#~ - :filename: MediaInfo.bat
|
21
|
+
#~ :filesize: 32
|
22
|
+
#~ :crc:
|
23
|
+
#~ :timestamp: 2010-09-03 09:10:45 +00:00
|
24
|
+
#~ - :filename: 7zxa.dll
|
25
|
+
#~ :filesize: 162816
|
26
|
+
#~ :crc:
|
27
|
+
#~ :timestamp: 2008-06-17 11:04:14 +00:00
|
28
|
+
#~ ##### error
|
29
|
+
- :filename: Readme.txt
|
30
|
+
:filesize: 1559
|
31
|
+
:crc: 2585723240
|
32
|
+
:attributes: 32
|
33
|
+
:timestamp: 2010-02-16 16:10:46 +01:00
|
34
|
+
data
|
35
|
+
|
36
|
+
<<rem
|
37
|
+
Mit Readme.txt hat es Fehler --> pruefen warum.
|
38
|
+
rem
|
39
|
+
|
40
|
+
#~ test = BC3::Snapshot.newh(YAML.load(extract))
|
41
|
+
test = BC3::Snapshot.newh(YAML.load(DATA))
|
42
|
+
|
43
|
+
|
44
|
+
test.save('results/test_yaml.xxx')
|
45
|
+
test.save('results/test_yaml.bcss')
|
46
|
+
#~ test.save('results/test_yaml.bcssx')
|
47
|
+
|
48
|
+
__END__
|
49
|
+
:snapshot: C:\Program Files (x86)\Beyond Compare 3
|
50
|
+
:timestamp: 2011-01-16 01:00:57.656250 +01:00
|
51
|
+
:content:
|
52
|
+
#~ - :dirname: Helpers
|
53
|
+
#~ :timestamp: 2011-01-10 15:05:37 +01:00
|
54
|
+
#~ :attributes: 16
|
55
|
+
#~ :content:
|
56
|
+
#~ - :dirname: HtmlTidy
|
57
|
+
#~ :timestamp: 2010-12-27 13:08:10 +01:00
|
58
|
+
#~ :attributes: 16
|
59
|
+
#~ :content:
|
60
|
+
#~ - :filename: HtmlTidy.exe
|
61
|
+
#~ :filesize: 110592
|
62
|
+
#~ :crc: 2173815664
|
63
|
+
#~ :attributes: 32
|
64
|
+
#~ :timestamp: 2007-08-16 21:00:00 +02:00
|
65
|
+
#~ - :filename: XML_tidied.bat
|
66
|
+
#~ :filesize: 129
|
67
|
+
#~ :crc: 4093430901
|
68
|
+
#~ :attributes: 32
|
69
|
+
#~ :timestamp: 2009-08-19 06:41:00 +02:00
|
70
|
+
#~ - :filename: XML_tidied_config.txt
|
71
|
+
#~ :filesize: 161
|
72
|
+
#~ :crc: 404518302
|
73
|
+
#~ :attributes: 32
|
74
|
+
#~ :timestamp: 2009-08-19 06:32:00 +02:00
|
75
|
+
#~ - :filename: DOC_to_TXT.vbs
|
76
|
+
#~ :filesize: 1775
|
77
|
+
#~ :crc: 843128772
|
78
|
+
#~ :attributes: 32
|
79
|
+
#~ :timestamp: 2010-10-25 15:40:00 +02:00
|
80
|
+
#~ - :filename: MediaInfo.bat
|
81
|
+
#~ :filesize: 32
|
82
|
+
#~ :crc: 232131984
|
83
|
+
#~ :attributes: 32
|
84
|
+
#~ :timestamp: 2010-09-03 11:10:45 +02:00
|
85
|
+
#~ - :filename: MediaInfo.exe
|
86
|
+
#~ :filesize: 200192
|
87
|
+
#~ :crc: 3570952021
|
88
|
+
#~ :attributes: 32
|
89
|
+
#~ :timestamp: 2010-05-14 00:12:10 +02:00
|
90
|
+
#~ - :filename: PdfToText.exe
|
91
|
+
#~ :filesize: 663552
|
92
|
+
#~ :crc: 2411832728
|
93
|
+
#~ :attributes: 32
|
94
|
+
#~ :timestamp: 2009-07-09 12:01:20 +02:00
|
95
|
+
#~ - :filename: XLS_to_CSV_Single.vbs
|
96
|
+
#~ :filesize: 909
|
97
|
+
#~ :crc: 1591998383
|
98
|
+
#~ :attributes: 32
|
99
|
+
#~ :timestamp: 2010-11-04 11:21:10 +01:00
|
100
|
+
#~ - :dirname: Packers
|
101
|
+
#~ :timestamp: 2010-10-21 14:15:31 +02:00
|
102
|
+
#~ :attributes: 16
|
103
|
+
#~ :content:
|
104
|
+
#~ - :filename: cpio.wcx
|
105
|
+
#~ :filesize: 55296
|
106
|
+
#~ :crc: 2352348747
|
107
|
+
#~ :attributes: 32
|
108
|
+
#~ :timestamp: 2002-09-24 18:24:40 +02:00
|
109
|
+
#~ - :filename: Masks.dat
|
110
|
+
#~ :filesize: 26
|
111
|
+
#~ :crc: 592179890
|
112
|
+
#~ :attributes: 32
|
113
|
+
#~ :timestamp: 2010-10-21 14:15:27 +02:00
|
114
|
+
#~ - :filename: rpm.wcx
|
115
|
+
#~ :filesize: 56320
|
116
|
+
#~ :crc: 3202116200
|
117
|
+
#~ :attributes: 32
|
118
|
+
#~ :timestamp: 2002-12-02 15:09:12 +01:00
|
119
|
+
#~ - :filename: 7zxa.dll
|
120
|
+
#~ :filesize: 162816
|
121
|
+
#~ :crc: 1142077912
|
122
|
+
#~ :attributes: 32
|
123
|
+
#~ :timestamp: 2008-06-17 13:04:14 +02:00
|
124
|
+
#~ - :filename: BC3Key.txt
|
125
|
+
#~ :filesize: 375
|
126
|
+
#~ :crc: 1626513908
|
127
|
+
#~ :attributes: 32
|
128
|
+
#~ :timestamp: 2010-08-09 17:07:59 +02:00
|
129
|
+
#~ - :filename: BComp.com
|
130
|
+
#~ :filesize: 93184
|
131
|
+
#~ :crc: 1452076902
|
132
|
+
#~ :attributes: 32
|
133
|
+
#~ :timestamp: 2008-11-21 13:17:02 +01:00
|
134
|
+
#~ - :filename: BComp.exe
|
135
|
+
#~ :filesize: 89088
|
136
|
+
#~ :crc: 309807675
|
137
|
+
#~ :attributes: 32
|
138
|
+
#~ :timestamp: 2010-02-04 18:33:32 +01:00
|
139
|
+
#~ - :filename: BCompare.chm
|
140
|
+
#~ :filesize: 1372366
|
141
|
+
#~ :crc: 2005151591
|
142
|
+
#~ :attributes: 32
|
143
|
+
#~ :timestamp: 2011-01-10 14:41:44 +01:00
|
144
|
+
#~ - :filename: BCompare.exe
|
145
|
+
#~ :filesize: 9562256
|
146
|
+
#~ :crc: 1565394031
|
147
|
+
#~ :attributes: 32
|
148
|
+
#~ :timestamp: 2011-01-10 14:41:06 +01:00
|
149
|
+
#~ - :filename: BCShellEx.dll
|
150
|
+
#~ :filesize: 132096
|
151
|
+
#~ :crc: 1919929414
|
152
|
+
#~ :attributes: 32
|
153
|
+
#~ :timestamp: 2010-03-01 10:54:14 +01:00
|
154
|
+
#~ - :filename: BCShellEx64.dll
|
155
|
+
#~ :filesize: 390648
|
156
|
+
#~ :crc: 2879715418
|
157
|
+
#~ :attributes: 32
|
158
|
+
#~ :timestamp: 2010-03-01 10:54:14 +01:00
|
159
|
+
#~ - :filename: FastMM_FullDebugMode.dll
|
160
|
+
#~ :filesize: 129024
|
161
|
+
#~ :crc: 4192386136
|
162
|
+
#~ :attributes: 32
|
163
|
+
#~ :timestamp: 2007-11-20 13:10:27 +01:00
|
164
|
+
- :filename: License.html
|
165
|
+
:filesize: 8937
|
166
|
+
:crc: 1763902305
|
167
|
+
:attributes: 32
|
168
|
+
:timestamp: 2010-04-12 19:22:44 +02:00
|
169
|
+
#~ - :filename: Readme.txt
|
170
|
+
#~ :filesize: 1559
|
171
|
+
#~ :crc: 2585723240
|
172
|
+
#~ :attributes: 32
|
173
|
+
#~ :timestamp: 2010-02-16 16:10:46 +01:00
|
174
|
+
- :filename: unins000.dat
|
175
|
+
:filesize: 267122
|
176
|
+
:crc: 3852464717
|
177
|
+
:attributes: 32
|
178
|
+
:timestamp: 2011-01-10 15:05:37 +01:00
|
179
|
+
- :filename: unins000.exe
|
180
|
+
:filesize: 1191984
|
181
|
+
:crc: 2885606081
|
182
|
+
:attributes: 32
|
183
|
+
:timestamp: 2011-01-10 15:05:29 +01:00
|
184
|
+
- :filename: unrar.dll
|
185
|
+
:filesize: 178176
|
186
|
+
:crc: 819978712
|
187
|
+
:attributes: 32
|
188
|
+
:timestamp: 2009-07-06 16:33:30 +02:00
|
data/lib/bc3.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Build Snapshots for usage with Beyond compare 3
|
3
|
+
|
4
|
+
Beyond compare 3 is a file and folder comparison tool.
|
5
|
+
The folder compare allows to generate snapshots.
|
6
|
+
|
7
|
+
This gem allows an alternate way to generate a snapshot.
|
8
|
+
In opposite to BC3, this tools offers you possibilities to
|
9
|
+
manipulate the snapshot.
|
10
|
+
|
11
|
+
You may simulate changes in your folder structure,
|
12
|
+
merge snapshots...
|
13
|
+
|
14
|
+
More about Beyond compare at http://www.scootersoftware.com
|
15
|
+
|
16
|
+
This tool is a result of the following question:
|
17
|
+
http://www.scootersoftware.com/vbulletin/showthread.php?t=7210
|
18
|
+
|
19
|
+
=Examples for the usage of the gem
|
20
|
+
==Merge snapshots from multiple CD/DVDs.
|
21
|
+
See bin/bc3_merge.rb
|
22
|
+
|
23
|
+
You can expand a _sneakernet_ on multiple storage location.
|
24
|
+
|
25
|
+
Merge the snapshots of your safety copies in CD and
|
26
|
+
compare with your original filesystem.
|
27
|
+
|
28
|
+
See http://www.scootersoftware.com/support.php?zz=kb_sneakernet
|
29
|
+
|
30
|
+
==Build virtual snapshots
|
31
|
+
Build your own snapshot.
|
32
|
+
|
33
|
+
Prepare a virtual folder structure as you would like to have
|
34
|
+
and use BC3 to convert your real structure to your wishes.
|
35
|
+
|
36
|
+
==Scan for identic files.
|
37
|
+
<em>
|
38
|
+
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 dups.
|
39
|
+
</em>
|
40
|
+
http://www.scootersoftware.com/vbulletin/showpost.php?p=27736&postcount=4
|
41
|
+
|
42
|
+
With this gem, you can analyse a snapshot, copy the data in a hash
|
43
|
+
and search for duplicate CRCs
|
44
|
+
|
45
|
+
|
46
|
+
=Warning and Restrictions
|
47
|
+
Please don't
|
48
|
+
require 'bc3'
|
49
|
+
include BC3
|
50
|
+
You may get problems with rdefinition of File (See BC3::File).
|
51
|
+
|
52
|
+
==Restrictions
|
53
|
+
===File attributes
|
54
|
+
File attributes are not supported by BC3::Snapshot.newd
|
55
|
+
(How can I read them in ruby?)
|
56
|
+
|
57
|
+
====Extended File data
|
58
|
+
Not supported at snapshot creation.
|
59
|
+
|
60
|
+
Version read at snapshot parsing, but no UTF-8 support.
|
61
|
+
|
62
|
+
====Compression
|
63
|
+
Compressed snapshot can be read, but not stored
|
64
|
+
(but BC3 can handle uncompressed snapshots).
|
65
|
+
|
66
|
+
====File sizes
|
67
|
+
Maximum 2GB (more is supported by BC3, but not by this gem).
|
68
|
+
|
69
|
+
=end
|
70
|
+
require 'date'
|
71
|
+
require 'zlib'
|
72
|
+
|
73
|
+
require 'log4r'
|
74
|
+
$log = Log4r::Logger.new('BC3')
|
75
|
+
$log.outputters = Log4r::StdoutOutputter.new('log_stdout')
|
76
|
+
$log.level = Log4r::INFO
|
77
|
+
|
78
|
+
|
79
|
+
require 'bc3/helper'
|
80
|
+
require 'bc3/snapshot'
|
81
|
+
require 'bc3/folder'
|
82
|
+
require 'bc3/file'
|
83
|
+
require 'bc3/time'
|
84
|
+
|
85
|
+
require 'bc3/parse'
|
86
|
+
|
87
|
+
module BC3
|
88
|
+
VERSION = '0.1.0'
|
89
|
+
end
|
data/lib/bc3/file.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'bc3/helper'
|
2
|
+
module BC3
|
3
|
+
=begin rdoc
|
4
|
+
File definition for BC3 handling.
|
5
|
+
|
6
|
+
If you need the original File inside BC3-module, use
|
7
|
+
::File#new
|
8
|
+
::File#open
|
9
|
+
|
10
|
+
=end
|
11
|
+
class File
|
12
|
+
include Helper
|
13
|
+
KEYS = [:filename, :filesize ]
|
14
|
+
KEYS_OPTIONAL = [
|
15
|
+
:timestamp, :crc, :attributes,
|
16
|
+
:version,
|
17
|
+
]
|
18
|
+
|
19
|
+
=begin rdoc
|
20
|
+
Definition of a file in a snapshot.
|
21
|
+
|
22
|
+
Arguments are given in a hash.
|
23
|
+
Must contain KEYS and supports KEYS_OPTIONAL
|
24
|
+
=end
|
25
|
+
def initialize( args )
|
26
|
+
raise ArgumentError unless args.kind_of?(Hash)
|
27
|
+
KEYS.each{|key|
|
28
|
+
raise ArgumentError, "Missing Key #{key}" unless args.has_key?(key)
|
29
|
+
}
|
30
|
+
(args.keys - (KEYS + KEYS_OPTIONAL)).each{|key|
|
31
|
+
$log.warn("Undefined key #{key} for #{self.inspect}" )
|
32
|
+
}
|
33
|
+
@filename = args[:filename]
|
34
|
+
@filesize = args[:filesize]
|
35
|
+
@crc = args[:crc]
|
36
|
+
|
37
|
+
@timestamp = args[:timestamp] || Time.now
|
38
|
+
@attributes = args[:attributes] || Attrib::Archive
|
39
|
+
|
40
|
+
@version = args[:version] #-> Extended File
|
41
|
+
|
42
|
+
#Test content
|
43
|
+
raise ArgumentError, "timestamp is no time-object" unless @timestamp.kind_of?(Time)
|
44
|
+
end
|
45
|
+
|
46
|
+
=begin rdoc
|
47
|
+
Create a folder from file system.
|
48
|
+
|
49
|
+
The argument must contain:
|
50
|
+
* dirname
|
51
|
+
=end
|
52
|
+
def self.new_by_filename( filename )
|
53
|
+
$log.debug("Build file #{filename} from file system")
|
54
|
+
|
55
|
+
#fixme: attrib
|
56
|
+
#~ p ::File.stat(filename)
|
57
|
+
#~ Get attributes: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/68298
|
58
|
+
|
59
|
+
settings = {
|
60
|
+
filename: filename,
|
61
|
+
filesize: ::File.size(filename),
|
62
|
+
#~ attributes: ::File.stat(filename),
|
63
|
+
#The timestamp must be 'local time' -> ignore local time zone for generation
|
64
|
+
timestamp: ::File.mtime(filename) + Time.now.gmt_offset,
|
65
|
+
}
|
66
|
+
|
67
|
+
::File.open( filename, 'r'){|f|
|
68
|
+
settings[:crc] = Helper::crc32(f.read)
|
69
|
+
} #fixme crc if ...
|
70
|
+
return File.new( settings )
|
71
|
+
end
|
72
|
+
attr_reader :filename
|
73
|
+
|
74
|
+
def to_hash()
|
75
|
+
{
|
76
|
+
filename: @filename,
|
77
|
+
filesize: @filesize,
|
78
|
+
crc: @crc,
|
79
|
+
attributes: @attributes,
|
80
|
+
timestamp: @timestamp
|
81
|
+
}
|
82
|
+
end
|
83
|
+
def inspect()
|
84
|
+
"<BC3::File #{@filename}>"
|
85
|
+
end
|
86
|
+
|
87
|
+
=begin rdoc
|
88
|
+
Prepare for snapshot (bcss-file).
|
89
|
+
|
90
|
+
ID_FILE (0x02)
|
91
|
+
Represents a file on the system.
|
92
|
+
|
93
|
+
Name : ShortString
|
94
|
+
Last Modified : FileTime
|
95
|
+
DOS Attributes : UInt32
|
96
|
+
Size : Int32[+Int64]
|
97
|
+
If Size > 2GB, store as Int32(-1) followed by Int64
|
98
|
+
CRC32 : UInt32
|
99
|
+
=end
|
100
|
+
def bcss()
|
101
|
+
bcss = "".force_encoding('BINARY')
|
102
|
+
bcss << 2
|
103
|
+
bcss << @filename.size
|
104
|
+
bcss << @filename
|
105
|
+
bcss << fixnum2int64(@timestamp.time2ad)
|
106
|
+
#DOS Attributes
|
107
|
+
bcss << @attributes #little endian storage
|
108
|
+
bcss << 0
|
109
|
+
bcss << 0
|
110
|
+
bcss << 0
|
111
|
+
#Size
|
112
|
+
bcss << fixnum2int32(@filesize)
|
113
|
+
|
114
|
+
#CRC32
|
115
|
+
bcss << fixnum2int32(@crc || 0)
|
116
|
+
#~ bcss << 255
|
117
|
+
bcss
|
118
|
+
end
|
119
|
+
end #Folder
|
120
|
+
end #BC3
|
data/lib/bc3/folder.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
$:.unshift('..') if $0 == __FILE__
|
2
|
+
require 'bc3'
|
3
|
+
module BC3
|
4
|
+
=begin rdoc
|
5
|
+
=end
|
6
|
+
class Folder
|
7
|
+
include Helper
|
8
|
+
=begin rdoc
|
9
|
+
=end
|
10
|
+
def initialize( dirname, timestamp = Time.now, attributes = nil )
|
11
|
+
@dirname = dirname
|
12
|
+
$log.debug("Create folder #{@dirname}")
|
13
|
+
raise ArgumentError unless @dirname.is_a?(String)
|
14
|
+
|
15
|
+
@timestamp = timestamp || Time.now
|
16
|
+
raise ArgumentError, "No Timestamp for Folder" unless @timestamp.is_a?(Time)
|
17
|
+
|
18
|
+
@attributes = attributes || Attrib::Directory
|
19
|
+
|
20
|
+
@content = {}
|
21
|
+
end
|
22
|
+
=begin rdoc
|
23
|
+
Create a folder from a hash.
|
24
|
+
|
25
|
+
The argument must contain:
|
26
|
+
* dirname
|
27
|
+
|
28
|
+
It may contain:
|
29
|
+
* timestamp
|
30
|
+
* content (array of folders and files)
|
31
|
+
=end
|
32
|
+
def self.newh( data )
|
33
|
+
raise ArgumentError, "No hash given" unless data.is_a?(Hash)
|
34
|
+
raise ArgumentError, "dirname missing" unless data.has_key?(:dirname)
|
35
|
+
if data.has_key?(:content)
|
36
|
+
raise ArgumentError, "Content is no array" unless data[:content].is_a?(Array)
|
37
|
+
end
|
38
|
+
|
39
|
+
$log.debug("Build folder #{data[:dirname]} from hash")
|
40
|
+
(data.keys - [:dirname, :content, :timestamp, :attributes]).each{|key|
|
41
|
+
$log.warn("Undefined key #{key} for #{self.inspect}" )
|
42
|
+
}
|
43
|
+
|
44
|
+
folder = new( data[:dirname], data[:timestamp], data[:attributes] )
|
45
|
+
data[:content].each{| element |
|
46
|
+
if element.has_key?(:dirname)
|
47
|
+
folder << Folder.newh(element)
|
48
|
+
elsif element.has_key?(:filename)
|
49
|
+
folder << File.new(element)
|
50
|
+
else
|
51
|
+
raise ArgumentError, "element without dir/filename"
|
52
|
+
end
|
53
|
+
} if data[:content]
|
54
|
+
folder
|
55
|
+
end
|
56
|
+
|
57
|
+
=begin rdoc
|
58
|
+
Create a folder from file system.
|
59
|
+
|
60
|
+
The argument must contain:
|
61
|
+
* dirname
|
62
|
+
=end
|
63
|
+
def self.new_by_dirname( dirname )
|
64
|
+
#fixme: Pattern??
|
65
|
+
#fixme: attrib
|
66
|
+
$log.debug("Build folder #{dirname} from file system")
|
67
|
+
#The timestamp must be 'local time' -> ignore local time zone for generation
|
68
|
+
folder = new( dirname, ::File.mtime(dirname) + Time.now.gmt_offset )
|
69
|
+
Dir.chdir(dirname){
|
70
|
+
Dir['*'].each{|f|
|
71
|
+
if ::File.directory?(f)
|
72
|
+
folder << Folder.new_by_dirname(f)
|
73
|
+
elsif ::File.exist?(f)
|
74
|
+
folder << File.new_by_filename(f)
|
75
|
+
else
|
76
|
+
raise ArgumentError, "#{f} not found in #{dirname}"
|
77
|
+
end
|
78
|
+
}
|
79
|
+
}
|
80
|
+
folder
|
81
|
+
end
|
82
|
+
#Name of the folder
|
83
|
+
attr_reader :dirname
|
84
|
+
=begin rdoc
|
85
|
+
Add content to Folder.
|
86
|
+
The 'content' must support the bcss-method.
|
87
|
+
|
88
|
+
Content may be:
|
89
|
+
* another folder
|
90
|
+
* a file
|
91
|
+
=end
|
92
|
+
def << (content)
|
93
|
+
if !content.respond_to?( :bcss )
|
94
|
+
$log.error("Add unknown datatype as content #{content.inspect}")
|
95
|
+
raise ArgumentError, "Add unknown datatype as content #{content.inspect}"
|
96
|
+
end
|
97
|
+
|
98
|
+
key = content.respond_to?(:dirname) ? content.dirname : content.filename
|
99
|
+
if @content[key] #content already available
|
100
|
+
if @content[key].respond_to? :dirname and content.respond_to? :dirname
|
101
|
+
$log.debug("Mix into folder #{key} in folder #{self.inspect}")
|
102
|
+
@content[key].mixin(content)
|
103
|
+
else
|
104
|
+
$log.warn("Overwrite #{key} in folder #{self.inspect}")
|
105
|
+
@content[key] = content
|
106
|
+
end
|
107
|
+
else
|
108
|
+
@content[key] = content
|
109
|
+
$log.debug("Add to folder #{self.inspect}: #{content.inspect}") if $log.debug?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
=begin rdoc
|
113
|
+
Merge two folders.
|
114
|
+
The content of a folder is added to the actual content.
|
115
|
+
=end
|
116
|
+
def mixin( folder )
|
117
|
+
$log.info("Mix in content to folder #{@dirname} in folder #{self.inspect}")
|
118
|
+
folder.each{|key, content| self << content }
|
119
|
+
end
|
120
|
+
=begin rdoc
|
121
|
+
Loop on content of the folder and return path + object.
|
122
|
+
|
123
|
+
Options may be
|
124
|
+
* :files: return Files
|
125
|
+
* :folder: return Folders
|
126
|
+
* :recursive: return also subdirectories.
|
127
|
+
The result is not nested, pathes are complete
|
128
|
+
=end
|
129
|
+
def each(*options)
|
130
|
+
if options.empty?
|
131
|
+
extract = @content #take all
|
132
|
+
else
|
133
|
+
extract = {}
|
134
|
+
@content.each{|key, content|
|
135
|
+
case content
|
136
|
+
when File
|
137
|
+
extract[key] = content if options.include?(:files)
|
138
|
+
when Folder
|
139
|
+
extract[key] = content if options.include?(:folders)
|
140
|
+
else
|
141
|
+
raise "Internal error"
|
142
|
+
end
|
143
|
+
|
144
|
+
if options.include?(:recursive) and content.is_a?(Folder)
|
145
|
+
content.each(*options).each{|subkey, subcontent|
|
146
|
+
extract[[key, subkey].join('/')] = subcontent
|
147
|
+
}
|
148
|
+
end
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
if block_given?
|
153
|
+
extract.each{|key, content| yield key, content }
|
154
|
+
else
|
155
|
+
extract
|
156
|
+
end
|
157
|
+
end
|
158
|
+
#~ def values();@content.values;end #fixme
|
159
|
+
def inspect()
|
160
|
+
"<BC3::Folder #{@dirname}>"
|
161
|
+
end
|
162
|
+
=begin rdoc
|
163
|
+
Return the content in a hash.
|
164
|
+
=end
|
165
|
+
def to_hash()
|
166
|
+
#fixme: Array with needed keys?
|
167
|
+
{
|
168
|
+
dirname: @dirname,
|
169
|
+
timestamp: @timestamp,
|
170
|
+
attributes: @attributes,
|
171
|
+
content: @content.values.map{|x| x.to_hash }
|
172
|
+
}
|
173
|
+
end
|
174
|
+
=begin rdoc
|
175
|
+
Prepare for snapshot (bcss-file).
|
176
|
+
|
177
|
+
Each record starts with a single UByte ID value and then the data defined below.
|
178
|
+
|
179
|
+
ID_DIRECTORY (0x01)
|
180
|
+
Represents a directory on the system, or an expanded archive file.
|
181
|
+
|
182
|
+
Name : ShortString
|
183
|
+
Last Modified : FileTime
|
184
|
+
DOS Attributes : UInt32
|
185
|
+
|
186
|
+
|
187
|
+
ID_DIRECTORY_END (0xFF)
|
188
|
+
Represents the end of a directory listing. No data.
|
189
|
+
=end
|
190
|
+
def bcss()
|
191
|
+
bcss = "".force_encoding('BINARY')
|
192
|
+
bcss << 1
|
193
|
+
bcss << @dirname.size
|
194
|
+
bcss << @dirname
|
195
|
+
bcss << fixnum2int64(@timestamp.time2ad)
|
196
|
+
#DOS Attributes
|
197
|
+
bcss << @attributes
|
198
|
+
bcss << 0
|
199
|
+
bcss << 0
|
200
|
+
bcss << 0
|
201
|
+
|
202
|
+
@content.each{|key, data|
|
203
|
+
bcss << data.bcss
|
204
|
+
}
|
205
|
+
|
206
|
+
bcss << 255
|
207
|
+
bcss
|
208
|
+
end
|
209
|
+
end #Folder
|
210
|
+
end #BC3
|
211
|
+
|
212
|
+
if $0 == __FILE__
|
213
|
+
|
214
|
+
folder = BC3::Folder.new('x')
|
215
|
+
folder << BC3::Folder.new('x')
|
216
|
+
folder << BC3::File.new( filename: 'x2', filesize: 1)
|
217
|
+
folder << BC3::File.new( filename: 'x', filesize: 1)
|
218
|
+
|
219
|
+
puts folder.each
|
220
|
+
exit
|
221
|
+
|
222
|
+
require 'YAML'
|
223
|
+
folder = BC3::Folder.newh( YAML.load(%{
|
224
|
+
:dirname: dir
|
225
|
+
:content:
|
226
|
+
- :dirname: subdir
|
227
|
+
:content:
|
228
|
+
- :filename: subfile
|
229
|
+
:filesize: 10
|
230
|
+
- :filename: file1
|
231
|
+
:filesize: 5
|
232
|
+
- :filename: file2
|
233
|
+
:filesize: 15
|
234
|
+
}))
|
235
|
+
|
236
|
+
x = folder.each(:folders,:files, :recursive)
|
237
|
+
puts "=========="
|
238
|
+
puts x.keys
|
239
|
+
end
|