File Coverage

blib/lib/Unix/ConfigFile.pm
Criterion Covered Total %
statement 111 137 81.0
branch 46 88 52.2
condition 3 3 100.0
subroutine 23 24 95.8
pod 8 16 50.0
total 191 268 71.2


line stmt bran cond sub pod time code
1             package Unix::ConfigFile;
2              
3             # $Id: ConfigFile.pm,v 1.6 2000/05/02 15:49:19 ssnodgra Exp $
4              
5 1     1   33 use 5.004;
  1         3  
  1         41  
6 1     1   5 use strict;
  1         2  
  1         32  
7 1     1   4 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $LOCKEXT);
  1         2  
  1         81  
8 1     1   5 use Carp;
  1         2  
  1         105  
9 1     1   937 use IO::File;
  1         11254  
  1         162  
10 1     1   8 use Fcntl qw(:flock);
  1         2  
  1         144  
11 1     1   2139 use Text::Tabs;
  1         1143  
  1         2185  
12              
13             require Exporter;
14              
15             @ISA = qw(Exporter);
16             # Items to export into callers namespace by default. Note: do not export
17             # names by default without a very good reason. Use EXPORT_OK instead.
18             # Do not simply export all your public functions/methods/constants.
19             @EXPORT = qw(
20            
21             );
22             $VERSION = '0.06';
23              
24             # Package variables
25             my $SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/.";
26              
27             # Preloaded methods go here.
28              
29             # Create a new ConfigFile (or, more likely, a ConfigFile subclass) object.
30             # Opens the specified file and calls the read method (which will be located
31             # in the subclass package) to initialize the object data structures
32             sub new {
33 8     8 1 58728 my ($pkg, $filename, %opt) = @_;
34              
35             # Initialize the object reference
36 8         794 my $this = {
37             filename => $filename,
38             handle => undef,
39             locked => 0,
40             lockfh => undef,
41             lockfile => "$filename.lock",
42             locking => "dotlock",
43             mode => "r+",
44             seq => [ ]
45             };
46 8         68 bless $this, $pkg;
47              
48             # Set options
49 8 50       145 $this->lockfile($opt{lockfile}) if defined $opt{lockfile};
50 8 50       58 $this->locking($opt{locking}) if defined $opt{locking};
51 8 50       38 $this->mode($opt{mode}) if defined $opt{mode};
52              
53             # Get a filehandle
54 8         125 my $fh = new IO::File $this->filename, $this->mode;
55 8 50       1985 return undef unless defined($fh);
56 8         61 $this->fh($fh);
57              
58             # Do file locking - this must happen before read is called or we could
59             # end up with stale data in memory
60 8 50       24 if ($this->mode eq "r") {
61 0 0       0 $this->lock("shared") or return undef;
62             }
63             else {
64 8 100       68 $this->lock() or return undef;
65             }
66              
67             # Initialize object structure from the file
68 4 50       17 if (exists $opt{readopts}) {
69 0 0       0 $this->read($this->fh, $opt{readopts}) or return undef;
70             }
71             else {
72 4 50       13 $this->read($this->fh) or return undef;
73             }
74 4         22 return $this;
75             }
76              
77              
78             # Commit in-memory changes to disk
79             sub commit {
80 4     4 1 240 my ($this, %opt) = @_;
81              
82 4 50       70 return 0 if $this->mode eq "r";
83 4         16 my $tempname = $this->filename . ".tmp." . $$;
84 4 50       41 my $fh = new IO::File ">$tempname" or return 0;
85 4         1049 my ($mode, $uid, $gid) = (stat $this->fh)[2,4,5];
86 4         187 chown $uid, $gid, $tempname;
87 4         92 chmod $mode, $tempname;
88 4 50       18 if (exists $opt{writeopts}) {
89 0 0       0 $this->write($fh, $opt{writeopts}) or return 0;
90             }
91             else {
92 4 50       24 $this->write($fh) or return 0;
93             }
94 4         20 undef $fh;
95 4 50       325 if (defined $opt{backup}) {
96 0         0 rename $this->filename, $this->filename . $opt{backup};
97             }
98 4         15 return rename $tempname, $this->filename;
99             }
100              
101              
102             # This method is absolutely necessary to prevent leftover lock files
103             sub DESTROY {
104 4     4   10 my $this = shift;
105              
106 4 50       55 $this->unlock() or croak "Can't unlock file: $!";
107 0         0 $this->fh->close();
108             }
109              
110              
111             # Filename accessor
112             sub filename {
113 16     16 0 49 my $this = shift;
114 16 50       584 @_ ? $this->{filename} = shift : $this->{filename};
115             }
116              
117              
118             # Filehandle accessor
119             sub fh {
120 16     16 0 35 my $this = shift;
121 16 100       198 @_ ? $this->{handle} = shift : $this->{handle};
122             }
123              
124              
125             # Locking method accessor
126             sub locking {
127 28     28 0 40 my $this = shift;
128              
129 28 50       143 return $this->{locking} unless @_;
130 0         0 my $lockmethod = shift;
131 0 0       0 return undef unless grep { $lockmethod eq $_ } qw(flock dotlock none);
  0         0  
132 0         0 $this->{locking} = $lockmethod;
133             }
134              
135              
136             # Lock filehandle accessor
137             sub lockfh {
138 4     4 0 11 my $this = shift;
139 4 50       22 @_ ? $this->{lockfh} = shift : $this->{lockfh};
140             }
141              
142              
143             # Lock file name accessor
144             sub lockfile {
145 8     8 0 13 my $this = shift;
146 8 50       63 @_ ? $this->{lockfile} = shift : $this->{lockfile};
147             }
148              
149              
150             # Mode accessor
151             sub mode {
152 20     20 0 34 my $this = shift;
153 20 50       239 return $this->{mode} unless @_;
154 0         0 my $mode = shift;
155 0 0       0 return undef unless grep { $mode eq $_ } qw(r r+ w);
  0         0  
156 0         0 $this->{mode} = $mode;
157             }
158              
159              
160             # Obtain a lock on the file. You can pass "shared" to request a shared lock;
161             # the default is exclusive. This function is somewhat inconsistent at the
162             # moment since it will block with the flock method but return an error if the
163             # dotlock method fails.
164             sub lock {
165 8     8 0 15 my $this = shift;
166              
167 8 50       64 return 1 if ($this->locking eq "none");
168 8 50       32 return 0 if $this->{locked};
169 8 50       22 if ($this->locking eq "flock") {
    50          
170 0 0       0 @_ ? flock $this->fh, LOCK_SH : flock $this->fh, LOCK_EX;
171             }
172             elsif ($this->locking eq "dotlock") {
173             # We only support exclusive locks with dotlock
174 8         43 my $fh = new IO::File $this->lockfile, O_CREAT|O_EXCL|O_RDWR;
175 8 100       2249 return 0 unless defined($fh);
176 4         36 $this->lockfh($fh);
177             }
178 4         15 $this->{locked} = 1;
179             }
180              
181              
182             # Unlock the file
183             sub unlock {
184 4     4 0 11 my $this = shift;
185              
186             # NOTE: Originally I wasn't unlinking the lock file unless the lock
187             # filehandle was defined. This led to the rather unexpected discovery
188             # the Perl would sometimes destroy the filehandle before destroying
189             # the object during program shutdown. Thus, we now check if locked
190             # is set, which happens only if a lock is successfully acquired.
191             # This also prevents us from unlinking someone else's lock file.
192              
193 4 50       12 return 1 if ($this->locking eq "none");
194 4 50       1812 return 0 unless $this->{locked};
195 0         0 $this->{locked} = 0;
196 0 0       0 if ($this->locking eq "flock") {
    0          
197 0         0 flock $this->fh, LOCK_UN;
198 0         0 return 1;
199             }
200             elsif ($this->locking eq "dotlock") {
201 0 0       0 $this->lockfh->close() if defined($this->lockfh);
202 0         0 my $result = unlink $this->lockfile;
203 0         0 return ($result == 1);
204             }
205             }
206              
207              
208             # Encrypts a plaintext password with a random salt
209             # This is provided for use with the subclasses
210             sub encpass {
211 0     0 1 0 my ($this, $pass) = @_;
212              
213 0         0 my $salt = substr($SALTCHARS, int(rand(length($SALTCHARS))), 1) .
214             substr($SALTCHARS, int(rand(length($SALTCHARS))), 1);
215 0         0 crypt($pass, $salt);
216             }
217              
218              
219             # Return the file sequence
220             sub sequence {
221 1     1 1 3 my $this = shift;
222 1         3 return @{$this->{seq}};
  1         21  
223             }
224              
225              
226             # Append information to the file sequence
227             sub seq_append {
228 65     65 1 85 my $this = shift;
229 65         63 push @{$this->{seq}}, @_;
  65         406  
230             }
231              
232              
233             # Insert information into the file sequence before the given data
234             sub seq_insert {
235 1     1 1 2 my $this = shift;
236 1         3 my $data = shift;
237              
238 1         3 for (my $i = 0; $i < @{$this->{seq}}; $i++) {
  64         156  
239 64 100       163 if ($this->{seq}[$i] eq $data) {
240 1         5 splice @{$this->{seq}}, $i, 0, @_;
  1         14  
241 1         6 return 1;
242             }
243             }
244 0         0 return 0;
245             }
246              
247              
248             # Remove the specified data from the file sequence
249             sub seq_remove {
250 2     2 1 13 my ($this, $data) = @_;
251              
252 2         6 for (my $i = 0; $i < @{$this->{seq}}; $i++) {
  68         243  
253 68 100       198 if ($this->{seq}[$i] eq $data) {
254 2         2 splice @{$this->{seq}}, $i, 1;
  2         10  
255 2         9 return 1;
256             }
257             }
258 0         0 return 0;
259             }
260              
261              
262             # Joinwrap is a utility function that happens to be useful in several modules
263             # This thing was a bitch to get working 100% right, so use caution. :-)
264             sub joinwrap {
265 36     36 1 99 my ($this, $linelen, $head, $indent, $delim, $tail, @list) = @_;
266              
267 36         46 my $result = "";
268 36         43 my $line = 0;
269 36         92 $linelen -= length(expand($tail));
270 36         652 while (@list) {
271 32 100       71 my $curline = $result ? $indent : $head;
272 32         51 $curline =~ s/%n/$line/;
273 32         39 my $appended = 0;
274 32   100     155 while (@list && length(expand($curline . $delim . $list[0])) <= $linelen) {
275 64 100       1319 $curline .= $delim if $appended;
276 64         98 $curline .= shift @list;
277 64         278 $appended++;
278             }
279             # Special case - element is longer than linelen
280 32 100       110 $curline .= shift @list unless $appended;
281             # Append newline if this isn't the first line
282 32 100       56 $result .= "\n" if $result;
283 32         58 $result .= $curline;
284             # Append tail unless this is the last line
285 32 100       60 $result .= $tail if @list;
286 32         73 $line++;
287             }
288 36 100       285 $result ? $result : $head;
289             }
290              
291              
292             # Autoload methods go after =cut, and are processed by the autosplit program.
293              
294             1;
295             __END__