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