File Coverage

blib/lib/Video/TeletextDB/Parameters.pm
Criterion Covered Total %
statement 24 140 17.1
branch 0 102 0.0
condition 0 21 0.0
subroutine 8 28 28.5
pod 0 20 0.0
total 32 311 10.2


line stmt bran cond sub pod time code
1             package Video::TeletextDB::Parameters;
2 1     1   1138 use 5.006001;
  1         4  
  1         36  
3 1     1   4 use strict;
  1         2  
  1         27  
4 1     1   4 use warnings;
  1         2  
  1         22  
5 1     1   5 use Carp;
  1         2  
  1         76  
6 1     1   6 use Fcntl qw(O_CREAT O_RDWR LOCK_EX LOCK_NB);
  1         2  
  1         48  
7 1     1   5 use POSIX qw(ENOENT);
  1         1  
  1         8  
8              
9             our $VERSION = "0.01";
10              
11             use Exporter::Tidy
12 1     1   121 Other => [qw(%default_parameters check_channel_name)];
  1         7  
  1         7  
13              
14             our %default_parameters =
15             (page_versions => undef,
16             want => undef,
17             RW => undef,
18             creat => undef,
19             umask => undef,
20             stale_period => 20 * 60,
21             expire_period => 2 * 24 * 60 * 60,
22             # blocking => 1,
23             channel => undef,
24             user_data => undef);
25              
26             sub new {
27 0 0   0 0   croak "$_[0] requires an even number of parameters" unless @_ % 2;
28 0           my $parameters = bless {}, shift;
29 0           my %params = @_;
30 0 0         $parameters->{parent} = delete $params{parent} if exists $params{parent};
31 0           $parameters->init(\%params);
32 0 0         croak("Unknown parameters ", join(", ", keys %params)) if %params;
33 0           return $parameters;
34             }
35              
36             sub check_channel_name {
37 0     0 0   my $channel = shift;
38 0 0         my $msg = !defined($channel) ? "Channel name is undefined" :
    0          
    0          
39             $channel eq "" ? "Channel name is empty" :
40             # Reasons:
41             # : ; \\ and / because they are used as component separators
42             # ' because it makes database quoting tricky if we ever go sql
43             # \0 because it stops parsing in systemcalls
44             $channel =~ m!([:;./\'\\\0])! ? "Channel '$channel' contains forbidden character '$1'" : return;
45 0 0         croak $msg unless shift;
46 0           return $msg;
47             }
48              
49             sub channels {
50 0   0 0 0   my $dir = shift->cache_dir || croak "No directory";
51 0 0         $dir =~ m!/\z! || croak "Directory '$dir' does not end with a /";
52 0 0         opendir(my $dh, $dir) || croak "Could not opendir $dir: $!";
53 0 0 0       return map(m!\A(.+)\.db\z!s && !check_channel_name($1, 1) &&
54             -f "$dir$_" && -r _ ? $1 : (), readdir($dh));
55             }
56              
57             sub has_channel {
58 0     0 0   my $tele = shift;
59 0 0         local $tele->{channel} = shift if @_;
60 0 0 0       return 1 if !check_channel_name($tele->{channel}, 1) &&
      0        
61             -f $tele->db_file && -r _;
62 0           return;
63             }
64              
65             sub init {
66 0     0 0   my ($parameters, $params) = @_;
67              
68 0           for (keys %default_parameters) {
69 0 0 0       my $val = exists $params->{$_} ? delete $params->{$_} :
70             $parameters->{parent} && $parameters->{parent}{$_};
71 0 0         if (defined $val) {
    0          
72 0           $parameters->{$_} = $val;
73             } elsif (defined $default_parameters{$_}) {
74 0           $parameters->{$_} = $default_parameters{$_};
75             }
76             }
77 0 0         if (defined($parameters->{page_versions})) {
78 0 0         $parameters->{page_versions} == int($parameters->{page_versions}) ||
79             croak "page_versions $parameters->{page_versions} should be a positive integer";
80 0 0         $parameters->{page_versions} >= 1 ||
81             croak "page_versions $parameters->{page_versions} should not be less than 1";
82 0 0         $parameters->{page_versions} <= 255 ||
83             croak "page_versions $parameters->{page_versions} should not be greater then 255";
84             }
85 0 0         check_channel_name($parameters->{channel}) if defined $parameters->{channel};
86             }
87              
88             sub channel {
89 0 0   0 0   return shift->{channel} unless @_ >= 2;
90              
91 0 0         croak "Too many arguments for channel method" if @_ > 2;
92 0           my ($parameters, $channel) = @_;
93 0 0         check_channel_name($channel) if defined($channel);
94              
95 0           my $old = $parameters->{channel};
96 0           $parameters->{channel} = $channel;
97 0           return $old;
98             }
99              
100             sub cache_dir {
101 0     0 0   my $parameters = shift;
102 0           croak "'$parameters' has no cache_dir method";
103             }
104              
105             sub db_file {
106 0     0 0   my $parameters = shift;
107 0 0         croak "No channel" unless defined($parameters->{channel});
108 0           return $parameters->cache_dir() . $parameters->{channel} . ".db";
109             }
110              
111             sub lock_file {
112 0     0 0   my $parameters = shift;
113 0 0         croak "No channel" unless defined($parameters->{channel});
114 0           return $parameters->cache_dir() . $parameters->{channel} . ".lock";
115             }
116              
117             sub want_file {
118 0     0 0   my $parameters = shift;
119 0 0         croak "No channel" unless defined($parameters->{channel});
120 0           return $parameters->cache_dir() . $parameters->{channel} . ".want";
121             }
122              
123             sub get_lock {
124 0     0 0   my $parameters = shift;
125 0           my $lock_file = shift;;
126 0 0 0       my $old_mask = $parameters->{creat} && defined $parameters->{umask} &&!shift() ?
127             umask($parameters->{umask}) : undef;
128 0           my $fh;
129 0           eval {
130 0           while (1) {
131             # Do double stats until the file on which we get the lock is
132             # actually the right one (in case people are deleting files)
133 0 0         sysopen($fh, $lock_file,
    0          
    0          
134             $parameters->{creat} ? O_RDWR | O_CREAT : O_RDWR) ||
135             croak("Could not open",
136             $parameters->{creat} ? "/create" : "",
137             " '$lock_file': $!");
138 0 0         my @stat = stat($fh) or croak "Could not fstat '$lock_file': $!";
139 0 0         flock($fh, LOCK_EX) || croak "Could not lock '$lock_file': $!";
140 0           my @new_stat = stat($lock_file);
141 0 0         if (@new_stat) {
    0          
142 0 0 0       return if $stat[0] == $new_stat[0] && $stat[1] == $new_stat[1];
143             } elsif ($! != ENOENT) {
144 0           croak "Could not restat '$lock_file': $!";
145             }
146             }
147             };
148 0           my $err = $@;
149 0 0         umask $old_mask if defined $old_mask;
150 0 0         die $err if $err;
151              
152 0           my $oldfh = select $fh;
153 0           $| = 1;
154 0           print "$$\n";
155 0           select $oldfh;
156 0           truncate $fh, tell($fh);
157 0           return $fh;
158             }
159              
160             sub lock : method {
161 0     0 0   my $parameters = shift;
162 0           return $parameters->get_lock($parameters->lock_file, @_);
163             }
164              
165             sub want {
166 0     0 0   my $parameters = shift;
167 0           return $parameters->get_lock($parameters->want_file, @_);
168             }
169              
170             my $code = "";
171             for my $name (keys %default_parameters) {
172 1     1   1474 no strict "refs";
  1         17  
  1         136  
173             next if *{$name}{CODE};
174             # if (defined $default_parameters{$name}) {
175             # $code .= "sub $name {
176             # croak 'Too many arguments for $name method' if \@_ > 1;
177             # return shift->{'$name'};
178             #}\n";
179             # } else {
180             $code .= "sub $name : method {
181             return shift->{'$name'} unless \@_ >= 2;
182             croak 'Too many arguments for $name method' if \@_ > 2;
183             my \$parameters = shift;
184             my \$old = \$parameters->{'$name'};
185             \$parameters->{'$name'} = shift;
186             return \$old;
187             }\n";
188             # }
189             }
190             # print STDERR $code;
191             if ($code) {
192 0 0   0 0   eval $code;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
193             die $@ if $@;
194             }
195              
196             1;
197             __END__