File Coverage

blib/lib/Tie/TextDir.pm
Criterion Covered Total %
statement 84 100 84.0
branch 29 58 50.0
condition 9 16 56.2
subroutine 17 18 94.4
pod n/a
total 139 192 72.4


line stmt bran cond sub pod time code
1             package Tie::TextDir;
2              
3 1     1   4910 use strict;
  1         2  
  1         42  
4 1     1   4 use File::Spec;
  1         2  
  1         19  
5 1     1   1005 use File::Temp;
  1         21575  
  1         65  
6 1     1   64 use Symbol;
  1         2  
  1         50  
7 1     1   4 use Fcntl qw(:DEFAULT);
  1         1  
  1         337  
8 1     1   7 use Carp;
  1         2  
  1         47  
9 1     1   4 use constant HAVE_56 => $] >= 5.006;
  1         2  
  1         47  
10 1     1   3 use vars qw($VERSION);
  1         2  
  1         1041  
11              
12             $VERSION = '0.06';
13              
14             sub TIEHASH {
15 4 50 33 4   527 croak "usage: tie(%hash, 'Tie::TextDir', \$path, [mode], [perms])"
16             unless 2 <= @_ and @_ <= 4;
17            
18 4         8 my ($package, $path, $mode, $perms) = @_;
19 4   100     9 $mode ||= 'ro';
20 4   50     13 $perms ||= 0775;
21 4         7 my $self = bless {}, $package;
22            
23             # Can we make changes to the database?
24 4 100       12 if ($mode eq 'rw') {
    50          
25 3         9 $self->{MODE} = O_CREAT | O_RDWR;
26             } elsif ($mode eq 'ro') {
27 1         3 $self->{MODE} = O_RDONLY;
28             } else {
29             # Assume $mode is a bitmask of Fcntl flags
30 0         0 $self->{MODE} = $mode;
31             }
32              
33             # Nice-ify $path:
34 4         6 $path =~ s#/$##;
35 4 50 66     45 croak "$path is not a directory" if -e $path and not -d _;
36 4 100       32 unless (-e $path) {
37 1 50       3 croak "$path does not exist" unless $self->{MODE} & O_CREAT;
38 1 50       48 mkdir $path, $perms or croak "Can't create $path: $!";
39             }
40 4         8 $self->{PATH} = $path;
41            
42             # Get a filehandle and open the directory:
43 4         5 $self->{HANDLE} = HAVE_56 ? undef : gensym();
44 4 50       84 opendir($self->{HANDLE}, $path) or croak("can't opendir $path: $!");
45            
46 4         498 return $self;
47             }
48              
49             sub FETCH {
50 5     5   52 my ($self, $key) = @_;
51 5 50       9 if ( !$self->_key_okay($key) ) {
52 0 0       0 carp "Bad key '$key'" if $^W;
53 0         0 return;
54             }
55              
56 5         39 my $file = File::Spec->catfile($self->{PATH}, $key);
57 5 50       54 return unless -e $file;
58              
59 5         12 local *FH;
60 5 50       123 unless (open( FH, "< $file" )) {
61 0         0 carp "Can't open $file for reading: $!";
62 0         0 return;
63             }
64 5         8 my $value;
65 5         59 sysread FH, $value, ( stat FH )[7];
66 5         39 close FH;
67 5         19 return $value;
68             }
69              
70              
71             sub STORE {
72 3     3   60 my ($self, $key) = (shift, shift);
73 3         42 my $file = File::Spec->catfile($self->{PATH}, $key);
74 3 50       14 croak "No write access for '$file'" unless $self->{MODE} & O_RDWR;
75              
76 3 50       8 if ( !$self->_key_okay($key) ) {
77 0 0       0 carp "Bad key '$key'" if $^W;
78 0         0 return;
79             }
80              
81             # Use temp file for writing, and then rename to make the update atomic
82 3         13 my ($fh, $tmpname) = File::Temp::tempfile(DIR => $self->{PATH}, CLEANUP => 1);
83 3         870 print $fh $_[0];
84 3         159 close $fh;
85 3 50       150 rename ($tmpname, $file) or croak ("can't rename temp file $tmpname to $file: $!");
86             }
87              
88              
89             sub DELETE {
90 4     4   21 my ($self, $key) = @_;
91 4         36 my $file = File::Spec->catfile($self->{PATH}, $key);
92 4 50       12 croak "No write access for '$file'" unless $self->{MODE} & O_RDWR;
93            
94 4 100       7 if ( !$self->_key_okay($key) ) {
95 2 50       5 carp "Bad key '$key'" if $^W;
96 2         7 return;
97             }
98            
99 2 50       26 return unless -e $file;
100            
101 2         3 my $return;
102 2 50       8 $return = $self->FETCH($key) if defined wantarray; # Don't bother in void context
103            
104 2 50       125 unlink $file or croak "Couldn't delete $file: $!";
105 2         8 return $return;
106             }
107              
108             sub CLEAR {
109 0     0   0 my $self = shift;
110 0 0       0 croak "No write access for '$self->{PATH}'" unless $self->{MODE} & O_RDWR;
111            
112 0         0 rewinddir($self->{HANDLE});
113 0         0 my $entry;
114 0         0 while (defined ($entry = readdir($self->{HANDLE}))) {
115 0 0 0     0 next if $entry eq '.' or $entry eq '..';
116 0         0 my $file = File::Spec->catfile($self->{PATH}, $entry);
117 0 0       0 unlink $file or croak "can't remove $file: $!";
118             }
119             }
120              
121             sub EXISTS {
122 3     3   6 my ($self, $key) = @_;
123 3 50       7 if ( !$self->_key_okay($key) ) {
124 3 50       8 carp "Bad key '$key'" if $^W;
125 3         19 return;
126             }
127 0         0 return -e File::Spec->catfile($self->{PATH}, $key);
128             }
129              
130              
131             sub DESTROY {
132 4     4   606 closedir shift()->{HANDLE}; # Probably not necessary
133             }
134              
135              
136             sub FIRSTKEY {
137 3     3   36 my $self = shift;
138            
139 3         16 rewinddir $self->{HANDLE};
140 3         4 my $entry;
141 3         54 while (defined ($entry = readdir($self->{HANDLE}))) {
142 4 100 100     31 return $entry unless ($entry eq '.' or $entry eq '..');
143             }
144 1         4 return;
145             }
146              
147              
148             sub NEXTKEY {
149 7     7   25 return readdir shift()->{HANDLE};
150             }
151              
152             sub _key_okay {
153 15 100   15   61 return 0 if $_[1] =~ /^\.{0,2}$/;
154 10         25 return 1;
155             }
156              
157             1;
158              
159             __END__