bc3 0.1.0
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.
- 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
|