line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
=head1 NAME |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
XBase::Memo - Generic support for various memo formats |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=cut |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package XBase::Memo; |
9
|
|
|
|
|
|
|
|
10
|
11
|
|
|
11
|
|
176
|
use strict; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
565
|
|
11
|
11
|
|
|
11
|
|
7277
|
use XBase::Base; |
|
11
|
|
|
|
|
34
|
|
|
11
|
|
|
|
|
447
|
|
12
|
|
|
|
|
|
|
|
13
|
11
|
|
|
11
|
|
218
|
use vars qw( $VERSION @ISA ); |
|
11
|
|
|
|
|
104
|
|
|
11
|
|
|
|
|
10499
|
|
14
|
|
|
|
|
|
|
@ISA = qw( XBase::Base ); |
15
|
|
|
|
|
|
|
$VERSION = '1.02'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# Read header is called from open to fill the object structures |
18
|
|
|
|
|
|
|
sub read_header { |
19
|
7
|
|
|
7
|
0
|
11
|
my $self = shift; |
20
|
7
|
|
|
|
|
17
|
my %options = @_; |
21
|
|
|
|
|
|
|
|
22
|
7
|
|
|
|
|
11
|
my $header; |
23
|
7
|
|
|
|
|
41
|
my $read_h_len = $self->read($header, 512); |
24
|
7
|
50
|
33
|
|
|
34
|
unless ($read_h_len == 512 or $read_h_len == 24) { |
25
|
0
|
|
|
|
|
0
|
$self->Error("Error reading header of $self->{'filename'}: $!\n"); |
26
|
0
|
|
|
|
|
0
|
return; |
27
|
|
|
|
|
|
|
}; |
28
|
|
|
|
|
|
|
|
29
|
7
|
|
|
|
|
10
|
my ($next_for_append, $block_size, $version); |
30
|
7
|
|
|
|
|
18
|
my $filename = $self->{'filename'}; |
31
|
7
|
100
|
|
|
|
46
|
if ($filename =~ /\.fpt$/i) { |
|
|
50
|
|
|
|
|
|
32
|
1
|
|
|
|
|
4
|
($next_for_append, $block_size) = unpack 'N@6n', $header; |
33
|
1
|
|
|
|
|
2
|
$version = 5; |
34
|
1
|
|
|
|
|
4
|
bless $self, 'XBase::Memo::Fox'; |
35
|
|
|
|
|
|
|
} elsif ($filename =~ /\.smt$/i) { |
36
|
0
|
|
|
|
|
0
|
($next_for_append, $block_size) = unpack 'VV', $header; |
37
|
0
|
|
|
|
|
0
|
bless $self, 'XBase::Memo::Apollo'; |
38
|
|
|
|
|
|
|
} else { |
39
|
6
|
|
|
|
|
20
|
($next_for_append, $version, $block_size) |
40
|
|
|
|
|
|
|
= unpack 'V @16C @20v', $header; |
41
|
6
|
|
|
|
|
60
|
my $dbf_version = $options{'dbf_version'}; |
42
|
6
|
50
|
|
|
|
23
|
$dbf_version = 15 unless defined $dbf_version; |
43
|
6
|
50
|
33
|
|
|
38
|
if ((($dbf_version & 15) == 3) or $version == 3) { |
44
|
6
|
|
|
|
|
10
|
$block_size = 512; |
45
|
6
|
|
|
|
|
10
|
$version = 3; |
46
|
6
|
|
|
|
|
18
|
bless $self, 'XBase::Memo::dBaseIII'; |
47
|
|
|
|
|
|
|
} else { |
48
|
0
|
|
|
|
|
0
|
$version = 4; |
49
|
0
|
|
|
|
|
0
|
bless $self, 'XBase::Memo::dBaseIV'; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
7
|
50
|
|
|
|
22
|
$block_size = 512 if int($block_size) == 0; |
54
|
|
|
|
|
|
|
|
55
|
7
|
|
|
|
|
124
|
$next_for_append = int ((((-s $self->{'filename'}) - 1) / $block_size) + 1); |
56
|
|
|
|
|
|
|
|
57
|
7
|
|
|
|
|
13
|
@{$self}{ qw( next_for_append header_len record_len version ) } |
|
7
|
|
|
|
|
30
|
|
58
|
|
|
|
|
|
|
= ( $next_for_append, $block_size, $block_size, $version ); |
59
|
|
|
|
|
|
|
|
60
|
7
|
|
|
|
|
14
|
$self->{'memosep'} = $options{'memosep'}; |
61
|
|
|
|
|
|
|
|
62
|
7
|
|
|
|
|
54
|
1; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub write_record { |
66
|
6
|
|
|
6
|
1
|
10
|
my ($self, $num) = (shift, shift); |
67
|
6
|
|
|
|
|
13
|
my $length = length(join '', @_); |
68
|
6
|
|
|
|
|
12
|
my $record_len = $self->{'record_len'}; |
69
|
6
|
|
|
|
|
24
|
my $num_of_blocks = int (($length + $record_len - 1) / $record_len); |
70
|
6
|
|
|
|
|
29
|
$self->SUPER::write_record($num, @_); |
71
|
6
|
50
|
33
|
|
|
22
|
if ($num < 0 or $num > $self->last_record()) { |
72
|
6
|
|
|
|
|
19
|
my $packed = pack "V", $num + $num_of_blocks + 1; |
73
|
6
|
100
|
|
|
|
25
|
if (ref $self eq 'XBase::Memo::Fox') { |
74
|
2
|
|
|
|
|
5
|
$packed = pack "N", $num + $num_of_blocks + 1; |
75
|
|
|
|
|
|
|
} |
76
|
6
|
|
|
|
|
22
|
$self->SUPER::write_to(0, $packed); |
77
|
6
|
|
|
|
|
14
|
$self->{'next_for_append'} = $num + $num_of_blocks + 1; |
78
|
|
|
|
|
|
|
} |
79
|
6
|
|
|
|
|
9
|
$num; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub last_record { |
83
|
54
|
|
|
54
|
0
|
173
|
shift->{'next_for_append'} - 2; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub create { |
87
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
88
|
1
|
|
|
|
|
4
|
my %options = @_; |
89
|
1
|
50
|
|
|
|
8
|
$self->create_file($options{'name'}) or return; |
90
|
1
|
|
|
|
|
2
|
my $version = $options{'version'}; |
91
|
1
|
50
|
|
|
|
7
|
if ($options{'name'} =~ /\.smt$/i) { |
|
|
50
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# For xmt file (whatever they are) |
93
|
0
|
0
|
|
|
|
0
|
$self->write_to(0, pack 'VV a504', 1, 512, '') or return; |
94
|
|
|
|
|
|
|
} elsif ($version == 5) { |
95
|
|
|
|
|
|
|
# Fox fpt file |
96
|
0
|
0
|
|
|
|
0
|
$self->write_to(0, pack 'N a2 n a504', 1, '', 512, '') or return; |
97
|
|
|
|
|
|
|
} else { |
98
|
1
|
50
|
|
|
|
3
|
$version = 3 unless defined $version; |
99
|
1
|
50
|
|
|
|
4
|
$version = 0 if $version == 4; |
100
|
1
|
50
|
|
|
|
10
|
$self->write_to(0, pack 'VVa8Ca3va490', 1, 0, |
101
|
|
|
|
|
|
|
$options{'dbf_filename'}, $version, '', 512, '') |
102
|
|
|
|
|
|
|
or return; |
103
|
|
|
|
|
|
|
} |
104
|
1
|
|
|
|
|
6
|
$self->close(); |
105
|
1
|
|
|
|
|
5
|
return $self; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# ################################ |
110
|
|
|
|
|
|
|
# dBase III+ specific memo methods |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
package XBase::Memo::dBaseIII; |
113
|
|
|
|
|
|
|
|
114
|
11
|
|
|
11
|
|
72
|
use XBase::Base; |
|
11
|
|
|
|
|
18
|
|
|
11
|
|
|
|
|
554
|
|
115
|
11
|
|
|
11
|
|
52
|
use vars qw( @ISA ); |
|
11
|
|
|
|
|
18
|
|
|
11
|
|
|
|
|
5760
|
|
116
|
|
|
|
|
|
|
@ISA = qw( XBase::Memo ); |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub read_record { |
119
|
10
|
|
|
10
|
|
15
|
my ($self, $num) = @_; |
120
|
10
|
|
|
|
|
14
|
my $result = ''; |
121
|
10
|
|
|
|
|
27
|
my $last = $self->last_record(); |
122
|
10
|
100
|
|
|
|
31
|
if (not defined $self->{'memosep'}) { |
123
|
2
|
|
|
|
|
12
|
$self->{'memosep'} = "\x1a\x1a"; |
124
|
2
|
50
|
|
|
|
11
|
if (not defined $self->read_record($last)) { |
125
|
0
|
|
|
|
|
0
|
$self->{'memosep'} = "\x1a"; |
126
|
0
|
0
|
|
|
|
0
|
if (not defined $self->read_record($last)) { |
127
|
0
|
|
|
|
|
0
|
$self->{'memosep'} = "\x1a\x1a"; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
10
|
|
|
|
|
20
|
while ($num <= $last) { |
133
|
10
|
50
|
|
|
|
48
|
my $buffer = $self->SUPER::read_record($num, -1) or return; |
134
|
10
|
|
|
|
|
23
|
my $index = index($buffer, $self->{'memosep'}); |
135
|
10
|
50
|
|
|
|
22
|
if ($index >= 0) { |
136
|
10
|
|
|
|
|
59
|
return $result . substr($buffer, 0, $index); |
137
|
|
|
|
|
|
|
} |
138
|
0
|
|
|
|
|
0
|
$result .= $buffer; |
139
|
0
|
|
|
|
|
0
|
$num++; |
140
|
|
|
|
|
|
|
} |
141
|
0
|
|
|
|
|
0
|
return; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub write_record { |
145
|
4
|
|
|
4
|
|
9
|
my ($self, $num) = (shift, shift); |
146
|
4
|
|
|
|
|
6
|
my $type = shift; |
147
|
4
|
|
|
|
|
12
|
my $data = join "", @_, "\x1a\x1a"; |
148
|
4
|
50
|
33
|
|
|
15
|
if ($num >= 0 and $num <= $self->last_record()) { |
149
|
0
|
|
|
|
|
0
|
my $buffer = $self->read_record($num); |
150
|
0
|
0
|
|
|
|
0
|
if (defined $buffer) { |
151
|
0
|
|
|
|
|
0
|
my $length = length $buffer; |
152
|
0
|
|
|
|
|
0
|
my $record_len = $self->{'record_len'}; |
153
|
0
|
|
|
|
|
0
|
my $space_in_blocks = |
154
|
|
|
|
|
|
|
int (($length + $record_len - 3) / $record_len); |
155
|
0
|
|
|
|
|
0
|
my $len_in_blocks = |
156
|
|
|
|
|
|
|
int ((length($data) + $record_len - 1) / $record_len); |
157
|
0
|
0
|
|
|
|
0
|
if ($len_in_blocks > $space_in_blocks) { |
158
|
0
|
|
|
|
|
0
|
$num = $self->last_record() + 1; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
} else { |
162
|
4
|
|
|
|
|
18
|
$num = $self->last_record() + 1; |
163
|
|
|
|
|
|
|
} |
164
|
4
|
|
|
|
|
19
|
$self->SUPER::write_record($num, $data); |
165
|
4
|
|
|
|
|
10
|
$num; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# ################################ |
169
|
|
|
|
|
|
|
# dBase IV specific memo methods |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
package XBase::Memo::dBaseIV; |
172
|
|
|
|
|
|
|
|
173
|
11
|
|
|
11
|
|
68
|
use XBase::Base; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
318
|
|
174
|
11
|
|
|
11
|
|
49
|
use vars qw( @ISA ); |
|
11
|
|
|
|
|
34
|
|
|
11
|
|
|
|
|
8905
|
|
175
|
|
|
|
|
|
|
@ISA = qw( XBase::Memo ); |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub read_record { |
178
|
4
|
|
|
4
|
|
5
|
my ($self, $num) = @_; |
179
|
4
|
|
|
|
|
4
|
my $result = ''; |
180
|
4
|
|
|
|
|
6
|
my $last = $self->last_record; |
181
|
|
|
|
|
|
|
|
182
|
4
|
|
|
|
|
14
|
my $buffer = $self->SUPER::read_record($num, -1); |
183
|
4
|
50
|
|
|
|
10
|
if (not defined $buffer) { return; } |
|
0
|
|
|
|
|
0
|
|
184
|
4
|
|
|
|
|
5
|
my $unpackstr; |
185
|
4
|
50
|
|
|
|
9
|
if (ref $self eq 'XBase::Memo::Fox') { |
186
|
4
|
|
|
|
|
5
|
$unpackstr = 'NN'; |
187
|
|
|
|
|
|
|
} else { |
188
|
0
|
|
|
|
|
0
|
$unpackstr = 'VV'; |
189
|
0
|
0
|
|
|
|
0
|
return unless substr($buffer, 0, 4) eq "\xff\xff\x08\x00"; |
190
|
|
|
|
|
|
|
} |
191
|
4
|
|
|
|
|
7
|
my ($unused_id, $length) = unpack $unpackstr, $buffer; |
192
|
4
|
50
|
|
|
|
9
|
$length += 8 if ref $self eq 'XBase::Memo::Fox'; |
193
|
|
|
|
|
|
|
|
194
|
4
|
|
|
|
|
6
|
my $block_size = $self->{'record_len'}; |
195
|
4
|
50
|
|
|
|
8
|
if ($length < $block_size) { |
196
|
4
|
|
|
|
|
22
|
return substr $buffer, 8, $length - 8; |
197
|
|
|
|
|
|
|
} |
198
|
0
|
|
|
|
|
0
|
my $rest_length = $length - $block_size; |
199
|
0
|
|
|
|
|
0
|
my $rest_data = $self->SUPER::read_record($num + 1, $rest_length); |
200
|
0
|
0
|
|
|
|
0
|
if (not defined $rest_data) { return; } |
|
0
|
|
|
|
|
0
|
|
201
|
0
|
|
|
|
|
0
|
return substr($buffer, 8) . $rest_data; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub write_record { |
205
|
2
|
|
|
2
|
|
3
|
my ($self, $num) = (shift, shift); |
206
|
2
|
|
|
|
|
8
|
my $type = shift; |
207
|
2
|
|
|
|
|
5
|
my $data = join "", @_; |
208
|
2
|
|
|
|
|
3
|
my $length = (length $data) + 8; |
209
|
|
|
|
|
|
|
|
210
|
2
|
|
|
|
|
6
|
my $startfield = "\xff\xff\x08\x00" . pack('V', $length); |
211
|
2
|
50
|
|
|
|
6
|
if (ref $self eq 'XBase::Memo::Fox') { |
212
|
2
|
50
|
|
|
|
8
|
if ($type eq 'P') { $startfield = pack 'N', 0; } |
|
0
|
50
|
|
|
|
0
|
|
213
|
2
|
|
|
|
|
3
|
elsif ($type eq 'M') { $startfield = pack 'N', 1; } |
214
|
0
|
|
|
|
|
0
|
else { $startfield = pack 'N', 2; } |
215
|
2
|
|
|
|
|
5
|
$startfield .= pack 'N', ($length - 8); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
### $data = $startfield . $data . "\x1a\x1a"; |
218
|
2
|
|
|
|
|
4
|
$data = $startfield . $data; |
219
|
|
|
|
|
|
|
|
220
|
2
|
50
|
33
|
|
|
11
|
if ($num >= 0 and $num <= $self->last_record()) { |
221
|
0
|
|
|
|
|
0
|
my $buffer = $self->read_record($num); |
222
|
0
|
0
|
|
|
|
0
|
if (defined $buffer) { |
223
|
0
|
|
|
|
|
0
|
my $length = (length $buffer) - 8; |
224
|
0
|
|
|
|
|
0
|
my $record_len = $self->{'record_len'}; |
225
|
0
|
|
|
|
|
0
|
my $space_in_blocks = |
226
|
|
|
|
|
|
|
int (($length + $record_len - 11) / $record_len); |
227
|
0
|
|
|
|
|
0
|
my $len_in_blocks = |
228
|
|
|
|
|
|
|
int ((length($data) + $record_len - 1) / $record_len); |
229
|
0
|
0
|
|
|
|
0
|
if ($len_in_blocks > $space_in_blocks) { |
230
|
0
|
|
|
|
|
0
|
$num = $self->last_record() + 1; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
} else { |
233
|
0
|
|
|
|
|
0
|
$num = $self->last_record() + 1; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
} else { |
236
|
2
|
|
|
|
|
14
|
$num = $self->last_record() + 1; |
237
|
|
|
|
|
|
|
} |
238
|
2
|
|
|
|
|
7
|
my $fill = $self->{'record_len'} - (( length $data ) % $self->{'record_len'}); |
239
|
2
|
|
|
|
|
6
|
$data .= "\000" x $fill; |
240
|
2
|
|
|
|
|
9
|
$self->SUPER::write_record($num, $data); |
241
|
2
|
|
|
|
|
5
|
$num; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# ####################################### |
246
|
|
|
|
|
|
|
# FoxPro specific memo methods (fpt file) |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
package XBase::Memo::Fox; |
249
|
|
|
|
|
|
|
|
250
|
11
|
|
|
11
|
|
143
|
use XBase::Base; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
234
|
|
251
|
11
|
|
|
11
|
|
54
|
use vars qw( @ISA ); |
|
11
|
|
|
|
|
27
|
|
|
11
|
|
|
|
|
853
|
|
252
|
|
|
|
|
|
|
@ISA = qw( XBase::Memo::dBaseIV ); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# ####################################### |
255
|
|
|
|
|
|
|
# Apollo specific memo methods (smt file) |
256
|
|
|
|
|
|
|
# |
257
|
|
|
|
|
|
|
# This is a real hack! No documentation used but it works for all files |
258
|
|
|
|
|
|
|
# i have tested with. |
259
|
|
|
|
|
|
|
# Dirk Tostmann (tostmann@tiss.com) |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
package XBase::Memo::Apollo; |
262
|
|
|
|
|
|
|
|
263
|
11
|
|
|
11
|
|
65
|
use XBase::Base; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
278
|
|
264
|
11
|
|
|
11
|
|
56
|
use vars qw( @ISA ); |
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
3662
|
|
265
|
|
|
|
|
|
|
@ISA = qw( XBase::Memo::dBaseIV ); |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub read_record { |
268
|
0
|
|
|
0
|
|
|
my ($self, $num) = @_; |
269
|
0
|
|
|
|
|
|
my $result = ''; |
270
|
|
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
|
return if $num =~ /^\s+$/; |
272
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
my ($block, $len, $offset) = unpack('vVV', $num); |
274
|
0
|
|
|
|
|
|
$block *= 8; |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
$result = $self->read_from($offset * $block, $len); |
277
|
|
|
|
|
|
|
|
278
|
0
|
|
|
|
|
|
$result; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub write_record { |
282
|
0
|
|
|
0
|
|
|
my ($self, $num, $type) = (shift, shift, shift); |
283
|
0
|
|
|
|
|
|
my $data = join "", @_; |
284
|
0
|
|
|
|
|
|
my $length = length $data; |
285
|
0
|
|
|
|
|
|
$num = $self->SUPER::write_record($self->{'next_for_append'}, $data); |
286
|
0
|
0
|
0
|
|
|
|
if (defined $num and $num) { |
287
|
0
|
|
|
|
|
|
pack 'vVV', $self->{'block_length'} / 8 , $length, $num; |
288
|
|
|
|
|
|
|
} else { |
289
|
0
|
|
|
|
|
|
' ' x 10; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
1; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
__END__ |