use
5.006_000;
use
Fcntl
qw( :DEFAULT :flock :seek )
;
sub
new {
my
$class
=
shift
;
my
(
$args
) =
@_
;
my
$self
=
bless
{
autobless
=> 1,
autoflush
=> 1,
end
=> 0,
fh
=>
undef
,
file
=>
undef
,
file_offset
=> 0,
locking
=> 1,
locked
=> 0,
filter_store_key
=>
undef
,
filter_store_value
=>
undef
,
filter_fetch_key
=>
undef
,
filter_fetch_value
=>
undef
,
},
$class
;
foreach
my
$param
(
keys
%$self
) {
next
unless
exists
$args
->{
$param
};
$self
->{
$param
} =
$args
->{
$param
};
}
if
(
$self
->{fh} && !
$self
->{file_offset} ) {
$self
->{file_offset} =
tell
(
$self
->{fh} );
}
$self
->
open
unless
$self
->{fh};
return
$self
;
}
sub
open
{
my
$self
=
shift
;
my
$flags
= O_CREAT | O_BINARY;
if
( !-e
$self
->{file} || -w _ ) {
$flags
|= O_RDWR;
}
else
{
$flags
|= O_RDONLY;
}
my
$fh
;
sysopen
(
$fh
,
$self
->{file},
$flags
)
or
die
"DBM::Deep: Cannot sysopen file '$self->{file}': $!\n"
;
$self
->{fh} =
$fh
;
binmode
$fh
;
if
(
$self
->{autoflush}) {
my
$old
=
select
$fh
;
$|=1;
select
$old
;
}
return
1;
}
sub
close
{
my
$self
=
shift
;
if
(
$self
->{fh} ) {
close
$self
->{fh};
$self
->{fh} =
undef
;
}
return
1;
}
sub
size {
my
$self
=
shift
;
return
0
unless
$self
->{fh};
return
( (-s
$self
->{fh}) -
$self
->{file_offset} );
}
sub
set_inode {
my
$self
=
shift
;
unless
(
defined
$self
->{inode} ) {
my
@stats
=
stat
(
$self
->{fh});
$self
->{inode} =
$stats
[1];
$self
->{end} =
$stats
[7];
}
return
1;
}
sub
print_at {
my
$self
=
shift
;
my
$loc
=
shift
;
local
($/,$\);
my
$fh
=
$self
->{fh};
if
(
defined
$loc
) {
seek
(
$fh
,
$loc
+
$self
->{file_offset}, SEEK_SET );
}
if
( DEBUG ) {
my
$caller
=
join
':'
, (
caller
)[0,2];
my
$len
=
length
(
join
''
,
@_
);
warn
"($caller) print_at( "
. (
defined
$loc
?
$loc
:
'<undef>'
) .
", $len )\n"
;
}
print
(
$fh
@_
) or
die
"Internal Error (print_at($loc)): $!\n"
;
return
1;
}
sub
read_at {
my
$self
=
shift
;
my
(
$loc
,
$size
) =
@_
;
local
($/,$\);
my
$fh
=
$self
->{fh};
if
(
defined
$loc
) {
seek
(
$fh
,
$loc
+
$self
->{file_offset}, SEEK_SET );
}
if
( DEBUG ) {
my
$caller
=
join
':'
, (
caller
)[0,2];
warn
"($caller) read_at( "
. (
defined
$loc
?
$loc
:
'<undef>'
) .
", $size )\n"
;
}
my
$buffer
;
read
(
$fh
,
$buffer
,
$size
);
return
$buffer
;
}
sub
DESTROY {
my
$self
=
shift
;
return
unless
$self
;
$self
->
close
;
return
;
}
sub
request_space {
my
$self
=
shift
;
my
(
$size
) =
@_
;
my
$loc
=
$self
->{end};
$self
->{end} +=
$size
;
return
$loc
;
}
sub
copy_stats {
my
$self
=
shift
;
my
(
$temp_filename
) =
@_
;
my
@stats
=
stat
(
$self
->{fh} );
my
$perms
=
$stats
[2] & 07777;
my
$uid
=
$stats
[4];
my
$gid
=
$stats
[5];
chown
(
$uid
,
$gid
,
$temp_filename
);
chmod
(
$perms
,
$temp_filename
);
}
sub
flush {
my
$self
=
shift
;
my
$old_fh
=
select
$self
->{fh};
my
$old_af
= $|; $| = 1; $| =
$old_af
;
select
$old_fh
;
return
1;
}
sub
is_writable {
my
$self
=
shift
;
my
$fh
=
$self
->{fh};
return
unless
defined
$fh
;
return
unless
defined
fileno
$fh
;
local
$\ =
''
;
no
warnings;
local
$^W;
return
print
$fh
''
;
}
sub
lock_exclusive {
my
$self
=
shift
;
my
(
$obj
) =
@_
;
return
$self
->_lock(
$obj
, LOCK_EX );
}
sub
lock_shared {
my
$self
=
shift
;
my
(
$obj
) =
@_
;
return
$self
->_lock(
$obj
, LOCK_SH );
}
sub
_lock {
my
$self
=
shift
;
my
(
$obj
,
$type
) =
@_
;
$type
= LOCK_EX
unless
defined
$type
;
if
( $^O eq
'MSWin32'
|| $^O eq
'cygwin'
) {
$type
= LOCK_EX;
}
if
(!
defined
(
$self
->{fh})) {
return
; }
if
(
$self
->{locking}) {
if
(!
$self
->{locked}) {
flock
(
$self
->{fh},
$type
);
my
@stats
=
stat
(
$self
->{fh});
$self
->{end} =
$stats
[7];
if
(
defined
(
$self
->{inode}) &&
$stats
[1] !=
$self
->{inode}) {
$self
->
close
;
$self
->
open
;
$obj
->{engine}->setup(
$obj
);
flock
(
$self
->{fh},
$type
);
$self
->{end} = (
stat
(
$self
->{fh}))[7];
}
}
$self
->{locked}++;
return
1;
}
return
;
}
sub
unlock {
my
$self
=
shift
;
if
(!
defined
(
$self
->{fh})) {
return
; }
if
(
$self
->{locking} &&
$self
->{locked} > 0) {
$self
->{locked}--;
if
(!
$self
->{locked}) {
flock
(
$self
->{fh}, LOCK_UN);
return
1;
}
return
;
}
return
;
}
1;