File Coverage

blib/lib/Config/Trivial.pm
Criterion Covered Total %
statement 177 177 100.0
branch 105 120 87.5
condition 15 18 83.3
subroutine 19 19 100.0
pod 8 8 100.0
total 324 342 94.7


line stmt bran cond sub pod time code
1             # $Id: Trivial.pm 63 2014-05-23 09:42:15Z adam $
2              
3             package Config::Trivial;
4              
5 9     9   114428 use 5.010; # May work on earlier perls but I've not tested
  9         41  
  9         417  
6 9     9   11136 use utf8;
  9         138  
  9         56  
7 9     9   319 use strict;
  9         51  
  9         332  
8 9     9   53 use warnings;
  9         19  
  9         558  
9 9     9   50 use Carp;
  9         27  
  9         24403  
10              
11             our $VERSION = '0.81';
12             my ( $_package, $_file ) = caller;
13              
14             #
15             # NEW
16             #
17              
18             sub new {
19 21     21 1 11369 my $class = shift;
20 21         75 my %args = @_;
21 21   100     780 my $object = bless {
      100        
      100        
      33        
22             _config_file => $_file, # The Config file, default is caller
23             _self => 1, # Set Self Read
24             _error_message => q{}, # Error Messages
25             _configuration => {}, # Where the configuration data goes
26             _backup_char => q{~}, # Backup marker
27             _separator => q{ }, # Separator
28             _multi_file => 0, # Multi file mode
29             _debug => $args{debug} || 0, # Debugging (verbose) mode
30             _strict => $args{strict} || 0, # Strict mode
31             _no_check => $args{no_check} || 0, # Skip filesystem checks
32             },
33             ref $class || $class;
34              
35 21 100       80 if ( $args{config_file} ) {
36 7 100       29 croak "Unable to read config file $args{config_file}"
37             unless set_config_file( $object, $args{config_file} );
38             }
39 19         80 return $object;
40             }
41              
42             #
43             # SET_CONFIG_FILE
44             #
45              
46             sub set_config_file {
47 55     55 1 5632 my $self = shift;
48 55         88 my $configuration_file = shift;
49              
50 55 100       136 if ( ref $configuration_file ) {
51 19 100       57 if ( ref $configuration_file eq 'HASH' ) {
52 17         51 foreach my $sub_config_file ( sort keys %{$configuration_file} ) {
  17         111  
53 19         36 my $config_file = $configuration_file->{$sub_config_file};
54 19 100       38 if ( $config_file ) {
55 17 100       48 if (! $self->_check_file($config_file) ) {
56 3         16 return;
57             }
58             }
59             else {
60 2         10 return $self->_raise_error('File error: No file name supplied')
61             }
62             }
63 8         22 $self->{_config_file} = $configuration_file;
64 8         15 $self->{_self} = 0;
65 8         15 $self->{_multi_file} = 1;
66 8         228 return $self;
67             }
68             else {
69 2         296 croak 'ERROR: Can only deal with a hash references';
70             }
71             }
72             else {
73 36 100       101 if ( $self->_check_file($configuration_file) ) {
74 23         43 $self->{_config_file} = $configuration_file;
75 23         40 $self->{_self} = 0;
76 23         41 $self->{_multi_file} = 0;
77 23         92 return $self;
78             }
79             else {
80 9         426 return;
81             }
82             }
83             }
84              
85             #
86             # READ
87             #
88              
89             sub read {
90 18     18 1 59 my $self = shift;
91 18         1193 my $key = shift; # If there is a key, return only it's value
92              
93 18 100       70 if ( $self->{_multi_file} ) {
94 2         208 croak 'ERROR: Read can only deal with a single file';
95             }
96              
97 16         61 $self->_read_config( $self->{_config_file});
98              
99 14 100       67 return $self->{_configuration}->{$key} if $key;
100 8         43 return $self->{_configuration};
101             }
102              
103              
104             #
105             # MULTI_READ
106             #
107              
108             sub multi_read {
109 5     5 1 13 my $self = shift;
110 5         9 my $hash = shift; # If there is specific hash, return only it's value
111              
112 5 100       22 if ( ! $self->{_multi_file} ) {
113 2         546 croak 'ERROR: Multi_Read is for multiple configuration files';
114             }
115              
116 3         4 foreach my $config_key ( keys %{$self->{_config_file}} ) {
  3         10  
117 5         11 my $config_file = $self->{_config_file}->{$config_key};
118 5         660 $self->_read_config( $config_file, $config_key );
119             # return unless $self->_check_file( $config_file );
120             }
121              
122 3 100       13 return $self->{_configuration}->{$hash} if $hash;
123 2         8 return $self->{_configuration};
124             }
125              
126             #
127             # GET_CONFIGURATION
128             #
129              
130             sub get_configuration {
131 5     5 1 3708 my $self = shift;
132 5         20 my $key = shift;
133              
134 5 100       24 return $self->{_configuration}->{$key} if $key;
135 2         7 return $self->{_configuration};
136             }
137              
138             #
139             # SET_CONFIGURATION
140             #
141              
142             sub set_configuration {
143 13     13 1 1150 my $self = shift;
144 13         20 my $hash = shift;
145              
146 13 100       44 return $self->_raise_error('No configuration data')
147             unless $hash;
148 11 100       52 return $self->_raise_error('Configuration not a reference')
149             unless ref $hash;
150 7 100       35 return $self->_raise_error(q{Configuration data isn't a hash reference})
151             unless ref $hash eq 'HASH';
152              
153 1         2 $self->{_configuration} = $hash;
154 1         6 return $self;
155             }
156              
157             #
158             # WRITE
159             #
160              
161             sub write {
162 19     19 1 784 my $self = shift;
163 19         56 my %args = @_;
164              
165 19   100     115 my $settings = $args{'configuration'} || $self->{_configuration};
166              
167 19 100       411 croak 'ERROR: No settings hash to write.'
168             unless $settings;
169 17 50       165 croak 'ERROR: Settings not a hashref.'
170             unless ref $settings eq 'HASH';
171              
172 17   100     1483 my $file = $args{'config_file'} || $self->{_config_file};
173              
174 17 100       43 if ( $file ) {
175 14 100 66     80 if ( ( $_file eq $file )
176             || ( $0 eq $file ) )
177             {
178 9         30 return $self->_raise_error(
179             'Not allowed to write to the calling file.');
180             }
181             }
182             else {
183 3         659 croak 'File error: No file name supplied';
184             }
185              
186 5 100       228 if ( -e $file ) {
187 1 50       15 croak "ERROR: Insufficient permissions to write to: $file"
188             unless ( -w $file );
189 1 50       86 rename $file, $file . $self->{_backup_char}
190             or croak "ERROR: Unable to rename $file.";
191             }
192              
193 5 50       376697 open my $config, '>', $file
194             or croak "ERROR: Unable to write configuration file: $file";
195 5         13 print {$config}
  5         81  
196             "#\n#\tConfig file written by $_file\n#\tUsing Config::Trivial version $VERSION\n#\n\n";
197              
198 5         17 foreach my $setting ( keys %{$settings} ) {
  5         28  
199 21 100       64 if ( $setting =~ / / ) { # Check for spaces in keys
200 3 100       243 croak qq{ERROR: Setting key "$setting" contains an illegal space}
201             if $self->{_strict};
202 1 50       5 carp qq{WARNING: Setting key "$setting" contains an illegal space}
203             if $self->{_debug};
204 1         2 my $old_setting = $setting;
205 1         5 $setting =~ s/ /_/g;
206 1 50       6 croak 'ERROR: Unable to fix space in key, replacement key exists already'
207             if $settings->{$setting};
208 1 50       5 $settings->{$old_setting} = q{ } unless $settings->{$old_setting};
209 1         3 $settings->{$old_setting} =~ s/\\\s*$/\\ #/;
210 1 50       2 printf {$config} "$setting%s$settings->{$old_setting}\n",
  1         9  
211             length $old_setting >= 8 ? "\t" : "\t\t";
212 1         3 next;
213             }
214 18 100       42 $settings->{$setting} = q{ } unless $settings->{$setting};
215 18         44 $settings->{$setting} =~ s/\\\s*$/\\ #/;
216 18 100       1173 printf {$config} "$setting%s$settings->{$setting}\n",
  18         101  
217             length $setting >= 8 ? "\t" : "\t\t";
218             }
219              
220 3         108 my $time = localtime;
221 3         14 print {$config} "\n#\n#\tThis file written at $time\n#\n";
  3         10  
222 3         151 close $config;
223 3         33 return 1;
224             }
225              
226             #
227             # GET_ERROR
228             #
229              
230             sub get_error {
231 30     30 1 345 my $self = shift;
232 30         285 return $self->{_error_message};
233             }
234              
235             # #################
236             # Private Functions
237             # #################
238              
239             #
240             # Perform some file checks
241             #
242              
243             sub _check_file {
244 74     74   103 my $self = shift;
245 74         95 my $file = shift;
246              
247             # Skip ALL checks if no_check is set
248 74 100       202 if ( $self->{'_no_check'} ) {
249 12         42 return $self;
250             }
251              
252             # Check the filename before using - may be slow on some filesystems
253 62 100       137 return $self->_raise_error('File error: No file name supplied')
254             unless $file;
255 60 100       1245 return $self->_raise_error("File error: Cannot find $file")
256             unless -e $file;
257 51 100       131 return $self->_raise_error("File error: $file isn't a real file")
258             unless -f _;
259 49 50       233 return $self->_raise_error("File error: Cannot read file $file")
260             unless -r _;
261 49 100       128 return $self->_raise_error("File error: $file is zero bytes long")
262             if -z _;
263 42         156 return $self;
264             }
265              
266             #
267             # Open and read an individual config file
268             #
269              
270             sub _read_config {
271 21     21   32 my $self = shift;
272 21         62 my $file = shift;
273 21         170 my $f_key = shift;
274              
275 21 50       52 return unless $self->_check_file( $file );
276              
277 21 50       991 open my $config, '<', $file
278             or croak "ERROR: Unable to open configuration file: $file";
279              
280 21 100       92 if ( $self->{_self} )
281             { # We are now parsing the calling file for it's __DATA__ section
282 1         30 while ( <$config> ) {
283 118 100       298 last if /^__DATA__\s*$/;
284             }
285             }
286 21         693 while ( <$config> ) {
287 233 100       730 next if /^\s*#/; # Skip comment lines starting #
288 184 100       685 next if /^\s*\n/; # Skip any empty lines
289 127 100       331 last if /^__END__\s*$/; # Don't care what comes after this
290 119 100       580 if ( s/\\\s*$// ) { # Look for a continuation character
291 8         18 $_ .= <$config>; # If found then glue the lines together
292 8 50       32 redo unless eof $config;
293             }
294 111         294 $self->_process_line( $_, $., $f_key ); # Send the line off for processing
295             }
296 19         485 close $config;
297 19         95 return;
298             }
299              
300             #
301             # Raise error condition
302             #
303             sub _raise_error {
304 43     43   62 my $self = shift;
305 43         53 my $message = shift;
306              
307 43 100       3113 croak $message if $self->{_strict}; # STRICT: die with the message
308 25 100       308 carp $message if $self->{_debug}; # DEBUG: warn with the message
309 25         142 $self->{_error_message} = $message; # NORMAL: set the message
310 25         119 return;
311             }
312              
313             #
314             # Parse a line and add to Config structure
315             #
316             sub _process_line {
317 111     111   154 my $self = shift;
318 111         164 my $line = shift;
319 111         1894 my $line_no = shift;
320 111         137 my $f_key = shift;
321 111         117 my ( $key, $value );
322              
323 111         1029 chomp $line;
324 111         834 $line =~ s/^\s+|\s+$|\s*#+.*$//g; # Remove comments, and spaces at start or end
325 111         445 $line =~ s/\s+/ /g; # Multiple whitespace to one space globally
326              
327 111 50       7554 if ( $line ) {
328 111         398 ( $key, $value ) = split / /, $line, 2;
329             }
330 111 50       1154 if ( $key ) {
331 9     9   76 no warnings 'uninitialized';
  9         41  
  9         17231  
332 111         215 $key = lc _clean_string( $key );
333             }
334 111 100       317 if ( exists $self->{_configuration}->{$key} ) {
335 20 100       330 croak qq{ERROR: Duplicate key "$key" found in config file on line $line_no}
336             if $self->{_strict};
337 18 100       307 carp qq{WARNING: Duplicate key "$key" found in config file on line $line_no}
338             if $self->{_debug};
339             }
340 109 100       268 if ( $key ) {
341 105 100       191 if ( defined $value ) {
342 92 100       144 if ( $f_key ) {
343 26         77 $self->{_configuration}->{$f_key}->{$key} = $value;
344             }
345             else {
346 66         187 $self->{_configuration}->{$key} = $value;
347             }
348             }
349             else {
350 13 50       38 carp qq{WARNING: Key "$key" has no valid value, on line $line_no of the config file}
351             if $self->{_debug};
352 13 100       54 $self->{_configuration}->{$key} = undef unless $self->{_strict};
353             }
354             }
355 109         633 return;
356             }
357              
358             #
359             # Clean data up to make a key out of it
360             #
361             sub _clean_string {
362 111     111   171 my $input = shift;
363 111         112 my $output;
364              
365 111         164 $input =~ tr/\e\`\'"%//ds; # Remove less gross crud from the input
366 111 100       16416 $output = $1
367             if ( $input =~ /^([\^\$-=\?\/\w.:\\\s\@~\|]+)$/ ); # De-Taint the input line
368 111 100       620 $output =~ s/^\s+|\s+$//g if $output; # Remove spaces at start or end
369 111         307 return $output;
370             }
371              
372             1;
373              
374              
375             __END__