sub
new {
my
$class
=
shift
;
$class
=
ref
(
$class
) ||
$class
;
my
$self
=
shift
;
if
(!
defined
$self
) {
$self
= {}; }
$self
->{cache} = {};
$self
->{dirty} = 0;
$self
->{prefix} ||=
'/'
;
my
$use_cache
= 1;
if
(
$self
->{type} eq
'dir'
) {
$self
->{cache_file} = File::Spec->catdir(
$self
->{prefix},
File::Spec->rel2abs(
$self
->{path}),
'.spamassassin_cache'
);
my
@stat
=
stat
(
$self
->{cache_file});
@stat
or dbg(
"AIcache: no access to %s: %s"
,
$self
->{cache_file}, $!);
$self
->{cache_mtime} =
$stat
[9] || 0;
}
else
{
my
@split
= File::Spec->splitpath(
$self
->{path});
$self
->{cache_file} = File::Spec->catdir(
$self
->{prefix},
File::Spec->rel2abs(
$split
[1]),
join
(
'_'
,
'.spamassassin_cache'
,
$self
->{type},
$split
[2]));
my
@stat
=
stat
(
$self
->{cache_file});
@stat
or dbg(
"AIcache: no access to %s: %s"
,
$self
->{cache_file}, $!);
$self
->{cache_mtime} =
$stat
[9] || 0;
@stat
=
stat
(
$self
->{path});
@stat
or dbg(
"AIcache: no access to %s: %s"
,
$self
->{path}, $!);
if
(
$stat
[9] >
$self
->{cache_mtime}) {
$use_cache
= 0;
}
}
$self
->{cache_file} = File::Spec->canonpath(
$self
->{cache_file});
local
*CACHE
;
if
(!
$use_cache
) {
}
elsif
(!
open
(CACHE,
$self
->{cache_file})) {
dbg(
"AIcache: cannot open AI cache file (%s): %s"
,
$self
->{cache_file},$!);
}
else
{
for
($!=0;
defined
(
$_
=<CACHE>); $!=0) {
my
(
$k
,
$v
) =
split
(/\t/,
$_
);
next
unless
(
defined
$k
&&
defined
$v
);
$self
->{cache}->{
$k
} =
$v
;
}
defined
$_
|| $!==0 or
$!==EBADF ? dbg(
"AIcache: error reading from AI cache file: $!"
)
:
warn
"error reading from AI cache file: $!"
;
close
CACHE
or
die
"error closing AI cache file ("
.
$self
->{cache_file}.
"): $!"
;
}
bless
(
$self
,
$class
);
$self
;
}
sub
count {
my
(
$self
) =
@_
;
return
keys
%{
$self
->{cache}};
}
sub
check {
my
(
$self
,
$name
) =
@_
;
return
$self
->{cache}
unless
$name
;
$name
=
$self
->canon(
$name
);
return
$self
->{cache}->{
$name
};
}
sub
update {
my
(
$self
,
$name
,
$date
) =
@_
;
return
unless
$name
;
$name
=
$self
->canon(
$name
);
if
(!
exists
$self
->{cache}->{
$name
} ||
$self
->{cache}->{
$name
} !=
$date
) {
$self
->{cache}->{
$name
} =
$date
;
$self
->{dirty} = 1;
}
}
sub
finish {
my
(
$self
) =
@_
;
return
unless
$self
->{dirty};
eval
{
mkpath(dirname(
$self
->{cache_file}));
1;
} or
do
{
my
$eval_stat
= $@ ne
''
? $@ :
"errno=$!"
;
chomp
$eval_stat
;
warn
"cannot mkpath for AI cache file ($self->{cache_file}): $eval_stat\n"
;
};
my
$towrite
=
''
;
while
(
my
(
$k
,
$v
) =
each
%{
$self
->{cache}}) {
$towrite
.=
"$k\t$v\n"
;
}
{
local
$SIG
{
'INT'
} =
'IGNORE'
;
local
$SIG
{
'TERM'
} =
'IGNORE'
;
if
(!
open
(CACHE,
">"
.
$self
->{cache_file}))
{
warn
"creating AI cache file failed ("
.
$self
->{cache_file}.
"): $!"
;
}
else
{
print
CACHE
$towrite
or
warn
"error writing to AI cache file: $!"
;
close
CACHE
or
warn
"error closing AI cache file ("
.
$self
->{cache_file}.
"): $!"
;
}
}
return
;
}
sub
canon {
my
(
$self
,
$name
) =
@_
;
if
(
$self
->{type} eq
'dir'
) {
$name
= (File::Spec->splitpath(
$name
))[2];
}
else
{
$name
=~ s/^.+\.(\d+)$/$1/;
}
return
$name
;
}
1;