Video-TeletextDB
view release on metacpan or search on metacpan
lib/Video/TeletextDB/Access.pm view on Meta::CPAN
package Video::TeletextDB::Access;
use 5.006001;
use strict;
use warnings;
use Carp;
use DB_File;
use POSIX qw(ENOENT EWOULDBLOCK);
use Fcntl qw(F_GETFL O_CREAT O_RDWR O_RDONLY O_ACCMODE LOCK_NB LOCK_EX);
# use AutoLoader qw(AUTOLOAD);
use Video::TeletextDB::Constants qw(:BdbPrefixes :VTX :VBI DB_VERSION);
use Video::TeletextDB::Page qw(vote $epoch_time);
our $VERSION = "0.02";
use base qw(Video::TeletextDB::Parameters);
use Exporter::Tidy
functions => [qw(tilde)],
variables => [qw($default_cache_dir $default_page_versions)];
use constant MIN_STORES => 10000; # Must have at least 10000 stores
use constant DB_RO => "Video::TeletextDB::DB_RO";
use constant DB_RW => "Video::TeletextDB::DB_RW";
our @CARP_NOT = qw(Video::TeletextDB::Options);
our $default_cache_dir = "~/.TeletextDB/cache";
our $default_page_versions = 5;
# Database format:
# V. => a* (version)
# s. => NNN (start time, number of stores, last store time)
# S. => C (page_versions)
# c.nn (page, subpage) => CN (last_counter, last_time)
# There is a fake c."\xff"x4 at the end to make scanning easier
# p.nnC (page, subpage, counter) =>
# Na* (store time, join \xa, raw rows (without \xa))
# There is a fake p."\xff"x5 at the end to make scanning easier
sub tilde {
defined(my $file = shift) || croak "Undefined file";
my ($user, $rest) = $file =~ m!^~([^/]*)(.*)\z!s or return $file;
if ($user ne "") {
my @pw = getpwnam($user) or croak "Could not find user $user";
$user = $pw[7];
} elsif (!defined($user = $ENV{HOME})) {
my @pw = getpwuid($>) or
croak "Could not determine who you are";
$user = $pw[7];
}
croak "Home directory is the empty string" if $user eq "";
$user =~ s!/*\z!$rest!;
$user = "/" if $user eq "";
# Restore taintedness
return $user . substr($file, 0, 0);
}
# Prepare a directory to contain databases
sub prepare {
my ($class, $tele, $params) = @_;
my $mkpath = exists $params->{mkpath} ?
delete $params->{mkpath} : !exists $params->{cache_dir};
my $dir = delete $params->{cache_dir};
$dir = $default_cache_dir unless defined $dir;
$dir = tilde($dir);
if ($dir !~ m!\A/!) {
require Cwd;
my $prefix = Cwd::getcwd();
$dir = $prefix =~ m!/\z! ? $prefix . $dir : "$prefix/$dir";
}
$dir .= "/" unless $dir =~ m!/\z!;
if (!-d $dir) {
croak "No visible directory named '$dir'" unless $mkpath;
require File::Path;
my $old_mask = umask($tele->{umask}) if defined($tele->{umask});
eval { File::Path::mkpath($dir) };
my $err = $@;
umask($old_mask) if defined($tele->{umask});
die $err if $err;
}
$tele->{cache_dir} = $dir;
}
# Opening a db file with O_CREAT can give you RW access even if you didn't
# ask for that. Use this to fix the state.
sub db_maybe_rw {
my $db = shift->{db};
open(my $fh, "+<&", $db->fd) || croak "Could not dup db fileno: $!";
my $flags = fcntl($fh, F_GETFL, 0) ||
croak "Could not fcntl db handle: $!";
$flags &= O_ACCMODE;
return 0 if $flags == O_RDONLY;
croak "Don't know how to handle a database opened in mode $flags" unless
$flags == O_RDWR;
bless $db, DB_RW;
return 1;
}
sub db_check {
my $access = shift;
my $db = $access->{db};
if (!$db->get(VERSION, my $version)) {
croak("Wanted version ", DB_VERSION, " differs from current $version for ", $access->db_file) if $version ne DB_VERSION;
} else {
$db = $access->upgrade(1);
( run in 1.225 second using v1.01-cache-2.11-cpan-df04353d9ac )