Conversion is partially complete. Lots of tests are failing, though
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / File.pm
CommitLineData
460b1067 1package DBM::Deep::File;
2
2120a181 3use 5.006_000;
460b1067 4
5use strict;
065b45be 6use warnings FATAL => 'all';
460b1067 7
86867f3a 8use Fcntl qw( :DEFAULT :flock :seek );
460b1067 9
695c88b1 10use constant DEBUG => 0;
11
460b1067 12sub new {
13 my $class = shift;
14 my ($args) = @_;
15
16 my $self = bless {
359a01ac 17 autobless => 1,
2120a181 18 autoflush => 1,
460b1067 19 end => 0,
20 fh => undef,
21 file => undef,
22 file_offset => 0,
2120a181 23 locking => 1,
460b1067 24 locked => 0,
2120a181 25#XXX Migrate this to the engine, where it really belongs.
460b1067 26 filter_store_key => undef,
27 filter_store_value => undef,
28 filter_fetch_key => undef,
29 filter_fetch_value => undef,
30 }, $class;
31
32 # Grab the parameters we want to use
33 foreach my $param ( keys %$self ) {
34 next unless exists $args->{$param};
35 $self->{$param} = $args->{$param};
36 }
37
38 if ( $self->{fh} && !$self->{file_offset} ) {
39 $self->{file_offset} = tell( $self->{fh} );
40 }
41
42 $self->open unless $self->{fh};
43
44 return $self;
45}
46
47sub open {
48 my $self = shift;
49
633df1fd 50 # Adding O_BINARY should remove the need for the binmode below. However,
460b1067 51 # I'm not going to remove it because I don't have the Win32 chops to be
52 # absolutely certain everything will be ok.
e9b0b5f0 53 my $flags = O_CREAT | O_BINARY;
54
55 if ( !-e $self->{file} || -w _ ) {
56 $flags |= O_RDWR;
57 }
58 else {
59 $flags |= O_RDONLY;
60 }
460b1067 61
62 my $fh;
63 sysopen( $fh, $self->{file}, $flags )
64 or die "DBM::Deep: Cannot sysopen file '$self->{file}': $!\n";
65 $self->{fh} = $fh;
66
67 # Even though we use O_BINARY, better be safe than sorry.
68 binmode $fh;
69
70 if ($self->{autoflush}) {
71 my $old = select $fh;
72 $|=1;
73 select $old;
74 }
75
76 return 1;
77}
78
79sub close {
80 my $self = shift;
81
82 if ( $self->{fh} ) {
83 close $self->{fh};
84 $self->{fh} = undef;
85 }
86
87 return 1;
88}
89
7dcefff3 90sub set_inode {
91 my $self = shift;
92
2120a181 93 unless ( defined $self->{inode} ) {
7dcefff3 94 my @stats = stat($self->{fh});
95 $self->{inode} = $stats[1];
96 $self->{end} = $stats[7];
97 }
98
99 return 1;
100}
101
019404df 102sub print_at {
103 my $self = shift;
104 my $loc = shift;
105
106 local ($/,$\);
107
108 my $fh = $self->{fh};
7dcefff3 109 if ( defined $loc ) {
110 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
111 }
112
695c88b1 113 if ( DEBUG ) {
114 my $caller = join ':', (caller)[0,2];
115 my $len = length( join '', @_ );
116 warn "($caller) print_at( " . (defined $loc ? $loc : '<undef>') . ", $len )\n";
117 }
118
45f047f8 119 print( $fh @_ ) or die "Internal Error (print_at($loc)): $!\n";
019404df 120
121 return 1;
122}
123
7dcefff3 124sub read_at {
125 my $self = shift;
126 my ($loc, $size) = @_;
127
128 local ($/,$\);
129
130 my $fh = $self->{fh};
131 if ( defined $loc ) {
132 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
133 }
134
695c88b1 135 if ( DEBUG ) {
136 my $caller = join ':', (caller)[0,2];
137 warn "($caller) read_at( " . (defined $loc ? $loc : '<undef>') . ", $size )\n";
138 }
139
7dcefff3 140 my $buffer;
141 read( $fh, $buffer, $size);
142
143 return $buffer;
144}
145
460b1067 146sub DESTROY {
147 my $self = shift;
148 return unless $self;
149
150 $self->close;
151
152 return;
153}
154
019404df 155sub request_space {
156 my $self = shift;
157 my ($size) = @_;
158
7dcefff3 159 #XXX Do I need to reset $self->{end} here? I need a testcase
019404df 160 my $loc = $self->{end};
161 $self->{end} += $size;
162
163 return $loc;
164}
165
15ba72cc 166##
167# If db locking is set, flock() the db file. If called multiple
168# times before unlock(), then the same number of unlocks() must
169# be called before the lock is released.
170##
5c0756fc 171sub lock_exclusive {
172 my $self = shift;
173 my ($obj) = @_;
174 return $self->lock( $obj, LOCK_EX );
175}
176
177sub lock_shared {
178 my $self = shift;
179 my ($obj) = @_;
180 return $self->lock( $obj, LOCK_SH );
181}
182
15ba72cc 183sub lock {
184 my $self = shift;
185 my ($obj, $type) = @_;
42717e46 186
15ba72cc 187 $type = LOCK_EX unless defined $type;
188
45f047f8 189 #XXX This is a temporary fix for Win32 and autovivification. It
190 # needs to improve somehow. -RobK, 2008-03-09
191 if ( $^O eq 'MSWin32' || $^O eq 'cygwin' ) {
192 $type = LOCK_EX;
193 }
194
15ba72cc 195 if (!defined($self->{fh})) { return; }
196
45f047f8 197 #XXX This either needs to allow for upgrading a shared lock to an
198 # exclusive lock or something else with autovivification.
199 # -RobK, 2008-03-09
15ba72cc 200 if ($self->{locking}) {
201 if (!$self->{locked}) {
202 flock($self->{fh}, $type);
203
204 # refresh end counter in case file has changed size
205 my @stats = stat($self->{fh});
206 $self->{end} = $stats[7];
207
208 # double-check file inode, in case another process
209 # has optimize()d our file while we were waiting.
2120a181 210 if (defined($self->{inode}) && $stats[1] != $self->{inode}) {
15ba72cc 211 $self->close;
212 $self->open;
213
214 #XXX This needs work
215 $obj->{engine}->setup_fh( $obj );
216
217 flock($self->{fh}, $type); # re-lock
218
219 # This may not be necessary after re-opening
220 $self->{end} = (stat($self->{fh}))[7]; # re-end
221 }
222 }
223 $self->{locked}++;
224
225 return 1;
226 }
227
228 return;
229}
230
231##
232# If db locking is set, unlock the db file. See note in lock()
233# regarding calling lock() multiple times.
234##
235sub unlock {
236 my $self = shift;
237
238 if (!defined($self->{fh})) { return; }
239
240 if ($self->{locking} && $self->{locked} > 0) {
241 $self->{locked}--;
15ba72cc 242
a8d2331c 243 if (!$self->{locked}) {
244 flock($self->{fh}, LOCK_UN);
245 return 1;
246 }
247
248 return;
15ba72cc 249 }
250
251 return;
252}
253
2120a181 254sub flush {
25c7c8d6 255 my $self = shift;
256
2120a181 257 # Flush the filehandle
258 my $old_fh = select $self->{fh};
259 my $old_af = $|; $| = 1; $| = $old_af;
260 select $old_fh;
25c7c8d6 261
262 return 1;
263}
28394a1a 264
08164b50 265# Taken from http://www.perlmonks.org/?node_id=691054
6e6789b0 266sub is_writable {
267 my $self = shift;
08164b50 268
269 my $fh = $self->{fh};
270 return unless defined $fh;
271 return unless defined fileno $fh;
272 local $\ = ''; # just in case
273 no warnings; # temporarily disable warnings
274 local $^W; # temporarily disable warnings
275 return print $fh '';
6e6789b0 276}
277
278sub copy_stats {
279 my $self = shift;
280 my ($temp_filename) = @_;
281
282 my @stats = stat( $self->{fh} );
283 my $perms = $stats[2] & 07777;
284 my $uid = $stats[4];
285 my $gid = $stats[5];
286 chown( $uid, $gid, $temp_filename );
287 chmod( $perms, $temp_filename );
288}
289
460b1067 2901;
291__END__