File Coverage

blib/lib/ZConf/backends/file.pm
Criterion Covered Total %
statement 24 589 4.0
branch 0 210 0.0
condition 0 3 0.0
subroutine 8 25 32.0
pod 17 17 100.0
total 49 844 5.8


line stmt bran cond sub pod time code
1             package ZConf::backends::file;
2              
3 1     1   22718 use File::Path;
  1         2  
  1         65  
4 1     1   751 use File::BaseDir qw/xdg_config_home/;
  1         1254  
  1         63  
5 1     1   785 use Chooser;
  1         122181  
  1         85  
6 1     1   12 use warnings;
  1         2  
  1         31  
7 1     1   6 use strict;
  1         2  
  1         35  
8 1     1   1098 use ZML;
  1         5497  
  1         31  
9 1     1   8 use Sys::Hostname;
  1         2  
  1         62  
10 1     1   5 use base 'Error::Helper';
  1         2  
  1         4780  
11              
12             =head1 NAME
13              
14             ZConf::backends::file - A configuration system allowing for either file or LDAP backed storage.
15              
16             =head1 VERSION
17              
18             Version 2.1.0
19              
20             =cut
21              
22             our $VERSION = '2.1.0';
23              
24             =head1 SYNOPSIS
25              
26             use ZConf;
27              
28             #creates a new instance
29             my $zconf = ZConf->new();
30             ...
31              
32             =head1 METHODS
33              
34             =head2 new
35              
36             my $zconf=ZConf->(\%args);
37              
38             This initiates the ZConf object. If it can't be initiated, a value of undef
39             is returned. The hash can contain various initization options.
40              
41             When it is run for the first time, it creates a filesystem only config file.
42              
43             =head3 args hash
44              
45             =head4 self
46              
47             This is the copy of the ZConf object intiating it.
48              
49             =head4 zconf
50              
51             This is the variables found in the ~/.config/zconf.zml.
52              
53             my $zconfbe=ZConf::backends::file->new(\%args);
54             if($zconfbe->error){
55             warn('error: '.$zconf->error.":".$zconf->errorString);
56             }
57              
58             =cut
59              
60             #create it...
61             sub new {
62 0     0 1   my %args;
63 0 0         if(defined($_[1])){
64 0           %args= %{$_[1]};
  0            
65             };
66              
67             #The thing that will be returned.
68             #conf holds configs
69             #args holds the arguements passed to new as well as runtime parameters
70             #set contains what set is in use for any loaded config
71             #zconf contains the parsed contents of zconf.zml
72             #user is space reserved for what ever the user of this package may wish to
73             # use it for... if they ever find the need to or etc... reserved for
74             # the prevention of poeple shoving stuff into $self->{} where ever
75             # they please... probally some one still will... but this is intented
76             # to help minimize it...
77             #error this is undef if, otherwise it is a integer for the error in question
78             #errorString this is a string describing the error
79             #meta holds meta variable information
80 0           my $self = {conf=>{}, args=>\%args, set=>{}, zconf=>{}, user=>{}, error=>undef,
81             errorString=>"", meta=>{}, comment=>{}, module=>__PACKAGE__,
82             revision=>{}, locked=>{}, autoupdateGlobal=>1, autoupdate=>{}};
83 0           bless $self;
84 0           $self->{module}=~s/\:\:/\-/g;
85              
86             #####################################
87             #real in the stuff from the arguments
88             #make sure we have a ZConf object
89 0 0         if (!defined( $args{self} )) {
90 0           $self->{error}=47;
91 0           $self->{errorString}='No ZConf object passed';
92 0           $self->warn;
93 0           return $self;
94             }
95 0 0         if ( ref($args{self}) ne 'ZConf' ) {
96 0           $self->{error}=47;
97 0           $self->{errorString}='No ZConf object passed. ref returned "'.ref( $args{self} ).'"';
98 0           $self->warn;
99 0           return $self;
100             }
101 0           $self->{self}=$args{self};
102 0 0         if (!defined( $args{zconf} )) {
103 0           $self->{error}=48;
104 0           $self->{errorString}='No zconf.zml var hash passed';
105 0           $self->warn;
106 0           return $self;
107             }
108 0 0         if ( ref($args{zconf}) ne 'HASH' ) {
109 0           $self->{error}=48;
110 0           $self->{errorString}='No zconf.zml var hash passed. ref returned "'.ref( $args{zconf} ).'"';
111 0           $self->warn;
112 0           return $self;
113             }
114 0           $self->{zconf}=$args{zconf};
115             #####################################
116              
117 0 0         if (!defined( $self->{zconf}{'file/base'} )) {
118 0           $self->{args}{base}=xdg_config_home()."/zconf/";
119             }else {
120 0           $self->{args}{base}=$self->{zconf}{'file/base'};
121             }
122              
123             #do something if the base directory does not exist
124 0 0         if(! -d $self->{args}{base}){
125             #if the base diretory can not be created, exit
126 0 0         if(!mkdir($self->{args}{base})){
127 0           $self->{error}=46;
128 0           $self->{errorString}="'".$self->{args}{base}."' does not exist and could not be created.\n";
129 0           $self->warn;
130 0           return $self;
131             }
132             }
133              
134             #get what the file only arg should be
135             #this is a Perl boolean value
136 0 0         if(!defined($self->{zconf}{fileonly})){
137 0           $self->{zconf}->{args}{fileonly}="0";
138             }else{
139 0           $self->{args}{fileonly}=$self->{zconf}{fileonly};
140             }
141              
142 0           return $self;
143             }
144              
145             =head2 configExists
146              
147             This method methods exactly the same as configExists, but
148             for the file backend.
149              
150             No config name checking is done to verify if it is a legit name or not
151             as that is done in configExists. The same is true for calling errorblank.
152              
153             $zconfbe->configExistsFile("foo/bar");
154             if($zconf->error){
155             warn('error: '.$zconf->{error}.":".$zconf->errorString);
156             }
157              
158             =cut
159              
160             #checks if a file config exists
161             sub configExists{
162 0     0 1   my ($self, $config) = @_;
163              
164 0           $self->errorblank;
165              
166             #makes the path if it does not exist
167 0 0         if(!-d $self->{args}{base}."/".$config){
168 0           return 0;
169             }
170              
171 0           return 1;
172             }
173              
174             =head2 createConfig
175              
176             This methods just like createConfig, but is for the file backend.
177             This is not really meant for external use. The config name passed
178             is not checked to see if it is legit or not.
179              
180             $zconf->createConfigFile("foo/bar");
181             if($zconf->error){
182             warn('error: '.$zconf->error.":".$zconf->errorString);
183             }
184              
185             =cut
186              
187             #creates a new config file as well as the default set
188             sub createConfig{
189 0     0 1   my ($self, $config) = @_;
190              
191 0           $self->errorblank;
192              
193             #makes the path if it does not exist
194 0 0         if(!mkpath($self->{args}{base}."/".$config)){
195 0           $self->{error}=16;
196 0           $self->{errorString}="'".$self->{args}{base}."/".$config."' creation failed.";
197 0           $self->warn;
198 0           return undef;
199             }
200              
201 0           return 1;
202             }
203              
204             =head2 delConfig
205              
206             This removes a config. Any sub configs will need to removes first. If any are
207             present, this method will error.
208              
209             #removes 'foo/bar'
210             $zconf->delConfig('foo/bar');
211             if(defined($zconf->error)){
212             warn('error: '.$zconf->error."\n".$zconf->errorString);
213             }
214              
215             =cut
216              
217             sub delConfig{
218 0     0 1   my $self=$_[0];
219 0           my $config=$_[1];
220              
221 0           $self->errorlank;
222              
223             #return if this can't be completed
224 0 0         if (defined($self->{error})) {
225 0           return undef;
226             }
227              
228 0           my @subs=$self->getSubConfigs($config);
229             #return if there are any sub configs
230 0 0         if (defined($subs[0])) {
231 0           $self->{error}='33';
232 0           $self->{errorString}='Could not remove the config as it has sub configs';
233 0           $self->warn;
234 0           return undef;
235             }
236              
237             #makes sure it exists before continuing
238             #This will also make sure the config exists.
239 0           my $returned = $self->configExists($config);
240 0 0         if (defined($self->error)){
241 0           $self->{error}='12';
242 0           $self->{errorString}='The config, "'.$config.'", does not exist';
243 0           $self->warn;
244 0           return undef;
245             }
246              
247 0           my @sets=$self->getAvailableSets($config);
248 0 0         if (defined($self->error)) {
249 0           $self->warnString('getAvailableSetsFile set an error');
250 0           return undef;
251             }
252              
253             #goes through and removes each set before deleting
254 0           my $setsInt='0';#used for intering through @sets
255 0           while (defined($sets[$setsInt])) {
256             #removes a set
257 0           $self->delSet($config, $sets[$setsInt]);
258 0 0         if ($self->{error}) {
259 0           $self->warnString('delSetFileset an error');
260 0           return undef;
261             }
262 0           $setsInt++;
263             }
264              
265             #the path to the config
266 0           my $configpath=$self->{args}{base}."/".$config;
267              
268 0 0         if (!rmdir($configpath)) {
269 0           $self->{error}=29;
270 0           $self->{errorString}='"'.$configpath.'" could not be unlinked.';
271 0           $self->warn;
272 0           return undef;
273             }
274              
275 0           return 1;
276             }
277              
278             =head2 delSet
279              
280             This deletes a specified set, for the filesystem backend.
281              
282             Two arguements are required. The first one is the name of the config and the and
283             the second is the name of the set.
284              
285             $zconf->delSetFile("foo/bar", "someset");
286             if($zconf->error){
287             warn('error: '.$zconf->error.":".$zconf->errorString);
288             }
289              
290             =cut
291              
292             sub delSet{
293 0     0 1   my $self=$_[0];
294 0           my $config=$_[1];
295 0           my $set=$_[2];
296              
297 0           $self->errorblank;
298              
299             #return if no set is given
300 0 0         if (!defined($set)){
301 0           $self->{error}=24;
302 0           $self->{errorString}='$set not defined';
303 0           $self->warn;
304 0           return undef;
305             }
306              
307             #return if no config is given
308 0 0         if (!defined($config)){
309 0           $self->{error}=25;
310 0           $self->{errorString}='$config not defined';
311 0           $self->warn;
312 0           return undef;
313             }
314              
315             #the path to the config
316 0           my $configpath=$self->{args}{base}."/".$config;
317              
318             #returns with an error if it could not be set
319 0 0         if (!-d $configpath) {
320 0           $self->{error}=14;
321 0           $self->{errorString}='"'.$config.'" is not a directory or does not exist';
322 0           $self->warn;
323 0           return undef;
324             }
325            
326             #the path to the set
327 0           my $fullpath=$configpath."/".$set.'.set';
328              
329 0 0         if (!unlink($fullpath)) {
330 0           $self->{error}=29;
331 0           $self->{errorString}='"'.$fullpath.'" could not be unlinked.';
332 0           $self->warn;
333 0           return undef;
334             }
335              
336 0           return 1;
337             }
338              
339             =head2 getAvailableSets
340              
341             This is exactly the same as getAvailableSets, but for the file back end.
342             For the most part it is not intended to be called directly.
343              
344             my @sets = $zconf->getAvailableSets("foo/bar");
345             if($zconf->error){
346             warn('error: '.$zconf->error.":".$zconf->errorString);
347             }
348              
349             =cut
350              
351             #this gets a set for a given file backed config
352             sub getAvailableSets{
353 0     0 1   my ($self, $config) = @_;
354              
355 0           $self->errorblank;
356              
357             #returns 0 if the config does not exist
358 0 0         if (!-d $self->{args}{base}."/".$config) {
359 0           $self->{error}=14;
360 0           $self->{errorString}="'".$self->{args}{base}."/".$config."' does not exist.";
361 0           $self->warn;
362 0           return undef;
363             }
364              
365 0 0         if (!opendir(CONFIGDIR, $self->{args}{base}."/".$config)) {
366 0           $self->{error}=15;
367 0           $self->{errorString}="'".$self->{args}{base}."/".$config."' open failed.";
368 0           $self->warn;
369 0           return undef;
370             }
371 0           my @direntries=readdir(CONFIGDIR);
372 0           closedir(CONFIGDIR);
373              
374             #remove hidden files and directory recursors from @direntries
375 0           @direntries=grep(!/^\./, @direntries);
376 0           @direntries=grep(!/^\.\.$/, @direntries);
377 0           @direntries=grep(!/^\.$/, @direntries);
378              
379 0           my @sets=();
380              
381             #go though the list and return only files
382 0           my $int=0;
383 0           while (defined($direntries[$int])) {
384 0 0 0       if (
385             ( -f $self->{args}{base}."/".$config."/".$direntries[$int] ) &&
386             ( $direntries[$int] =~ /\.set$/ )
387             ){
388 0           $direntries[$int] =~ s/\.set$//g;
389 0           push(@sets, $direntries[$int]);
390             }
391 0           $int++;
392             }
393              
394 0           return @sets;
395             }
396              
397             =head2 getConfigRevision
398              
399             This fetches the revision for the speified config using
400             the file backend.
401              
402             A return of undef means that the config has no sets created for it
403             yet or it has not been read yet by 2.0.0 or newer.
404              
405             my $revision=$zconf->getConfigRevision('some/config');
406             if($zconf->error){
407             warn('error: '.$zconf->error.":".$zconf->errorString);
408             }
409             if(!defined($revision)){
410             print "This config has had no sets added since being created or is from a old version of ZConf.\n";
411             }
412              
413             =cut
414              
415             sub getConfigRevision{
416 0     0 1   my $self=$_[0];
417 0           my $config=$_[1];
418              
419 0           $self->errorblank;
420              
421             #return false if the config is not set
422 0 0         if (!defined($config)){
423 0           $self->{error}=25;
424 0           $self->{errorString}='No config specified';
425 0           $self->warn;
426 0           return undef;
427             }
428              
429             #checks to make sure the config does exist
430 0 0         if(!$self->configExists($config)){
431 0           $self->{error}=12;
432 0           $self->{errorString}="'".$config."' does not exist.";
433 0           $self->warn;
434 0           return undef;
435             }
436              
437             #
438 0           my $revisionfile=$self->{args}{base}."/".$config."/.revision";
439              
440 0           my $revision;
441 0 0         if ( -f $revisionfile) {
442 0 0         if(!open("THEREVISION", '<', $revisionfile)){
443 0           $self->warnString("'".$revisionfile."' open failed");
444             }
445 0           $revision=join('', );
446 0           close(THEREVISION);
447             }
448              
449 0           return $revision;
450             }
451              
452             =head2 getSubConfigs
453              
454             This gets any sub configs for a config. "" can be used to get a list of configs
455             under the root.
456              
457             One arguement is accepted and that is the config to look under.
458              
459             #lets assume 'foo/bar' exists, this would return
460             my @subConfigs=$zconf->getSubConfigs("foo");
461             if($zconf->error){
462             warn('error: '.$zconf->error.":".$zconf->errorString);
463             }
464              
465             =cut
466              
467             #gets the configs under a config
468             sub getSubConfigs{
469 0     0 1   my ($self, $config)= @_;
470              
471 0           $self->errorblank;
472              
473             #returns 0 if the config does not exist
474 0 0         if(!-d $self->{args}{base}."/".$config){
475 0           $self->{error}=14;
476 0           $self->{errorString}="'".$self->{args}{base}."/".$config."' does not exist.";
477 0           $self->warn;
478 0           return undef;
479             }
480              
481 0 0         if(!opendir(CONFIGDIR, $self->{args}{base}."/".$config)){
482 0           $self->{error}=15;
483 0           $self->{errorString}="'".$self->{args}{base}."/".$config."' open failed.";
484 0           $self->warn;
485 0           return undef;
486             }
487 0           my @direntries=readdir(CONFIGDIR);
488 0           closedir(CONFIGDIR);
489              
490             #remove, ""^."" , ""."" , and "".."" from @direntries
491 0           @direntries=grep(!/^\./, @direntries);
492 0           @direntries=grep(!/^\.\.$/, @direntries);
493 0           @direntries=grep(!/^\.$/, @direntries);
494              
495 0           my @sets=();
496              
497             #go though the list and return only files
498 0           my $int=0;
499 0           while(defined($direntries[$int])){
500 0 0         if(-d $self->{args}{base}."/".$config."/".$direntries[$int]){
501 0           push(@sets, $direntries[$int]);
502             };
503 0           $int++;
504             }
505              
506 0           return @sets;
507             }
508              
509             =head2 isConfigLocked
510              
511             This checks if a config is locked or not for the file backend.
512              
513             One arguement is required and it is the name of the config.
514              
515             The returned value is a boolean value.
516              
517             my $locked=$zconf->isConfigLockedFile('some/config');
518             if($zconf->error){
519             warn('error: '.$zconf->error.":".$zconf->errorString);
520             }
521             if($locked){
522             print "The config is locked\n";
523             }
524              
525             =cut
526              
527             sub isConfigLocked{
528 0     0 1   my $self=$_[0];
529 0           my $config=$_[1];
530              
531 0           $self->errorblank;
532              
533             #return false if the config is not set
534 0 0         if (!defined($config)){
535 0           $self->{error}=25;
536 0           $self->{errorString}='No config specified';
537 0           $self->warn;
538 0           return undef;
539             }
540              
541             #makes sure it exists
542 0           my $exists=$self->configExists($config);
543 0 0         if ($self->{error}) {
544 0           $self->warnString('configExists errored');
545 0           return undef;
546             }
547 0 0         if (!$exists) {
548 0           $self->{error}=12;
549 0           $self->{errorString}='The config, "'.$config.'", does not exist';
550 0           $self->warn;
551 0           return undef;
552             }
553              
554             #checks if it is
555 0           my $lockfile=$self->{args}{base}."/".$config."/.lock";
556 0 0         if (-e $lockfile) {
557             #it is locked
558 0           return 1;
559             }
560              
561 0           return 0;
562             }
563              
564             =head2 read
565              
566             readFile methods just like read, but is mainly intended for internal use
567             only. This reads the config from the file backend.
568              
569             =head3 hash args
570              
571             =head4 config
572              
573             The config to load.
574              
575             =head4 override
576              
577             This specifies if override should be ran not.
578              
579             If this is not specified, it defaults to 1, true.
580              
581             =head4 set
582              
583             The set for that config to load.
584              
585             $zconf->readFile({config=>"foo/bar"})
586             if($zconf->error){
587             warn('error: '.$zconf->error.":".$zconf->errorString);
588             }
589              
590             =cut
591              
592             #read a config from a file
593             sub read{
594 0     0 1   my $self=$_[0];
595 0           my %args=%{$_[1]};
  0            
596              
597 0           $self->errorblank;
598              
599             #return false if the config is not set
600 0 0         if (!defined($args{config})){
601 0           $self->{error}=25;
602 0           $self->{errorString}='$config not defined';
603 0           $self->warn;
604 0           return undef;
605             }
606              
607             #return false if the config is not set
608 0 0         if (!defined($args{set})){
609 0           $self->{error}=24;
610 0           $self->{errorString}='$arg{set} not defined';
611 0           $self->warn;
612 0           return undef;
613             }
614              
615             #default to overriding
616 0 0         if (!defined($args{override})) {
617 0           $args{override}=1;
618             }
619              
620 0           my $fullpath=$self->{args}{base}."/".$args{config}."/".$args{set}.'.set';
621              
622             #return false if the full path does not exist
623 0 0         if (!-f $fullpath){
624 0           return 0;
625             }
626              
627             #retun from a this if a comma is found in it
628 0 0         if( $args{config} =~ /,/){
629 0           return 0;
630             }
631              
632 0 0         if(!open("thefile", $fullpath)){
633 0           return 0;
634             };
635 0           my @rawdataA=;
636 0           close("thefile");
637            
638 0           my $rawdata=join('', @rawdataA);
639            
640             #gets it
641 0           my $zml=ZML->new;
642              
643             #parses it
644 0           $zml->parse($rawdata);
645 0 0         if ($zml->{error}) {
646 0           $self->{error}=28;
647 0           $self->{errorString}='$zml->parse errored. $zml->{error}="'.$zml->{error}.'" '.
648             '$zml->{errorString}="'.$zml->{errorString}.'"';
649 0           $self->warn;
650 0           return undef;
651             }
652              
653             #at this point we save the stuff in it
654 0           $self->{self}->{conf}{$args{config}}=\%{$zml->{var}};
  0            
655 0           $self->{self}->{meta}{$args{config}}=\%{$zml->{meta}};
  0            
656 0           $self->{self}->{comment}{$args{config}}=\%{$zml->{comment}};
  0            
657              
658             #sets the set that was read
659 0           $self->{self}->{set}{$args{config}}=$args{set};
660              
661             #updates the revision
662 0           my $revisionfile=$self->{args}{base}."/".$args{config}."/.revision";
663             #opens the file and returns if it can not
664             #creates it if necesary
665 0 0         if ( -f $revisionfile) {
666 0 0         if(!open("THEREVISION", '<', $revisionfile)){
667 0           $self->warnString(':43: '."'".$revisionfile."' open failed");
668 0           $self->{revision}{$args{config}}=time.' '.hostname.' '.rand();
669             }
670 0           $self->{revision}{$args{config}}=join('', );
671 0           close(THEREVISION);
672             }else {
673 0           $self->{revision}{$args{config}}=time.' '.hostname.' '.rand();
674             #tag it with a revision if it does not have any...
675 0 0         if(!open("THEREVISION", '>', $revisionfile)){
676 0           $self->{error}=43;
677 0           $self->{errorString}="'".$revisionfile."' open failed";
678 0           $self->warn;
679 0           return undef;
680             }
681 0           print THEREVISION $self->{revision}{$args{config}};
682 0           close("THEREVISION");
683             }
684              
685             #checks if it is locked or not and save it
686 0           my $locked=$self->isConfigLocked($args{config});
687 0 0         if ($locked) {
688 0           $self->{locked}{$args{config}}=1;
689             }
690              
691             #run the overrides if requested tox
692 0 0         if ($args{override}) {
693             #runs the override if not locked
694 0 0         if (!$locked) {
695 0           $self->{self}->override({ config=>$args{config} });
696             }
697             }
698              
699 0           return $self->{self}->{revision}{$args{config}};
700             }
701              
702             =head2 readChooser
703              
704             This methods just like readChooser, but methods on the file backend
705             and only really intended for internal use.
706              
707             my $chooser = $zconf->readChooserFile("foo/bar");
708             if($zconf->error){
709             warn('error: '.$zconf->error.":".$zconf->errorString);
710             }
711              
712             =cut
713              
714             #this gets the chooser for a the config... for the file backend
715             sub readChooser{
716 0     0 1   my ($self, $config)= @_;
717              
718 0           $self->errorblank;
719              
720             #return false if the config is not set
721 0 0         if (!defined($config)){
722 0           $self->{error}=25;
723 0           $self->{errorString}='$config not defined';
724 0           $self->warn;
725 0           return undef;
726             }
727              
728             #make sure the config name is legit
729 0           my ($error, $errorString)=$self->{self}->configNameCheck($config);
730 0 0         if(defined($error)){
731 0           $self->{error}=$error;
732 0           $self->{errorString}=$errorString;
733 0           $self->warn;
734 0           return undef;
735             }
736            
737             #checks to make sure the config does exist
738 0 0         if(!$self->configExists($config)){
739 0           $self->{error}=12;
740 0           $self->{errorString}="'".$config."' does not exist.";
741 0           $self->warn;
742 0           return undef;
743             }
744              
745             #the path to the file
746 0           my $chooser=$self->{args}{base}."/".$config."/.chooser";
747              
748             #if the chooser does not exist, turn true, but blank
749 0 0         if(!-f $chooser){
750 0           return "";
751             }
752              
753             #open the file and get the string error on not being able to open it
754 0 0         if(!open("READCHOOSER", $chooser)){
755 0           $self->{error}=15;
756 0           $self->{errorString}="'".$self->{args}{base}."/".$config."/.chooser' read failed.";
757 0           $self->warn;
758 0           return undef;
759             }
760 0           my $chooserstring=;
761 0           close("READCHOOSER");
762              
763 0           return ($chooserstring);
764             }
765              
766             =head2 setExists
767              
768             This checks if the specified set exists.
769              
770             Two arguements are required. The first arguement is the name of the config.
771             The second arguement is the name of the set. If no set is specified, the default
772             set is used. This is done by calling 'defaultSetExists'.
773              
774             my $return=$zconf->setExists("foo/bar", "fubar");
775             if($zconf->error){
776             warn('error: '.$zconf->error.":".$zconf->errorString);
777             }else{
778             if($return){
779             print "It exists.\n";
780             }
781             }
782              
783             =cut
784              
785             sub setExists{
786 0     0 1   my ($self, $config, $set)= @_;
787              
788             #blank any errors
789 0           $self->errorblank;
790              
791             #this will get what set to use if it is not specified
792 0 0         if (!defined($set)) {
793 0           return $self->defaultSetExists($config);
794 0 0         if ($self->error) {
795 0           $self->warnString('No set specified and defaultSetExists errored');
796 0           return undef;
797             }
798             }
799              
800             #We don't do any config name checking here or even if it exists as getAvailableSets
801             #will do that.
802              
803 0           my @sets = $self->getAvailableSets($config);
804 0 0         if (defined($self->{error})) {
805 0           return undef;
806             }
807              
808              
809 0           my $setsInt=0;#used for intering through $sets
810             #go through @sets and check for matches
811 0           while (defined($sets[$setsInt])) {
812             #return true if the current one matches
813 0 0         if ($sets[$setsInt] eq $set) {
814 0           return 1;
815             }
816              
817 0           $setsInt++;
818             }
819              
820             #if we get here, it means it was not found in the loop
821 0           return undef;
822             }
823              
824             =head2 setLockConfig
825              
826             This unlocks or logs a config for the file backend.
827              
828             Two arguements are taken. The first is a
829             the config name, required, and the second is
830             if it should be locked or unlocked
831              
832             #lock 'some/config'
833             $zconf->setLockConfigFile('some/config', 1);
834             if($zconf->error){
835             warn('error: '.$zconf->error.":".$zconf->errorString);
836             }
837              
838             #unlock 'some/config'
839             $zconf->setLockConfigFile('some/config', 0);
840             if($zconf->error){
841             warn('error: '.$zconf->error.":".$zconf->errorString);
842             }
843              
844             #unlock 'some/config'
845             $zconf->setLockConfigFile('some/config');
846             if($zconf->error){
847             warn('error: '.$zconf->error.":".$zconf->errorString);
848             }
849              
850             =cut
851              
852             sub setLockConfig{
853 0     0 1   my $self=$_[0];
854 0           my $config=$_[1];
855 0           my $lock=$_[2];
856              
857 0           $self->errorblank;
858              
859             #return false if the config is not set
860 0 0         if (!defined($config)){
861 0           $self->{error}=25;
862 0           $self->{errorString}='No config specified';
863 0           $self->warn;
864 0           return undef;
865             }
866              
867             #makes sure it exists
868 0           my $exists=$self->configExists($config);
869 0 0         if ($self->{error}) {
870 0           $self->warnString('configExists errored');
871 0           return undef;
872             }
873 0 0         if (!$exists) {
874 0           $self->{error}=12;
875 0           $self->{errorString}='The config, "'.$config.'", does not exist';
876 0           $self->warn;
877 0           return undef;
878             }
879              
880             #locks the config
881 0           my $lockfile=$self->{args}{base}."/".$config."/.lock";
882              
883             #handles locking it
884 0 0         if ($lock) {
885 0 0         if(!open("THELOCK", '>', $lockfile)){
886 0           $self->{error}=44;
887 0           $self->{errorString}="'".$lockfile."' open failed";
888 0           $self->warn;
889 0           return undef;
890             }
891 0           print THELOCK time."\n".hostname;
892 0           close("THELOCK");
893             #return now that it is locked
894 0           return 1;
895             }
896              
897             #handles unlocking it
898 0 0         if (-e $lockfile) { #don't error if it is already unlocked
899 0 0         if (!unlink($lockfile)) {
900 0           $self->{error}=44;
901 0           $self->{errorString}='"'.$lockfile.'" could not be unlinked.';
902 0           $self->warn;
903 0           return undef;
904             }
905             }
906              
907 0           return 1;
908             }
909              
910             =head2 writeChooser
911              
912             This method is a internal method and largely meant to only be called
913             writeChooser, which it methods the same as. It works on the file backend.
914              
915             $zconf->writeChooserFile("foo/bar", $chooserString)
916             if($zconf->error){
917             warn('error: '.$zconf->error.":".$zconf->errorString);
918             }
919              
920             =cut
921              
922             sub writeChooser{
923 0     0 1   my ($self, $config, $chooserstring)= @_;
924              
925 0           $self->errorblank;
926              
927             #return false if the config is not set
928 0 0         if (!defined($config)){
929 0           $self->{error}=25;
930 0           $self->{errorString}='$config not defined';
931 0           $self->warn;
932 0           return undef;
933             }
934              
935             #checks if it is locked or not
936 0           my $locked=$self->isConfigLocked($config);
937 0 0         if ($self->{error}) {
938 0           $self->warnString('isconfigLockedFile errored');
939 0           return undef;
940             }
941 0 0         if ($locked) {
942 0           $self->{error}=45;
943 0           $self->{errorString}='The config "'.$config.'" is locked';
944 0           $self->warn;
945 0           return undef;
946             }
947              
948             #return false if the config is not set
949 0 0         if (!defined($chooserstring)){
950 0           $self->{error}=40;
951 0           $self->{errorString}='\$chooserstring not defined';
952 0           $self->warn;
953 0           return undef;
954             }
955              
956             #make sure the config name is legit
957 0           my ($error, $errorString)=$self->{self}->configNameCheck($config);
958 0 0         if(defined($error)){
959 0           $self->{error}=$error;
960 0           $self->{errorString}=$errorString;
961 0           $self->warn;
962 0           return undef;
963             }
964              
965 0           my $chooser=$self->{args}{base}."/".$config."/.chooser";
966              
967             #open the file and get the string error on not being able to open it
968 0 0         if(!open("WRITECHOOSER", ">", $chooser)){
969 0           $self->{error}=15;
970 0           $self->{errorString}="'".$self->{args}{base}."/".$config."/.chooser' open failed.";
971 0           $self->warn;
972             }
973 0           print WRITECHOOSER $chooserstring;
974 0           close("WRITECHOOSER");
975              
976 0           return (1);
977             }
978              
979             =head2 writeSetFromHash
980              
981             This takes a hash and writes it to a config for the file backend.
982             It takes two arguements, both of which are hashes.
983              
984             The first hash contains
985              
986             The second hash is the hash to be written to the config.
987              
988             =head2 args hash
989              
990             =head3 config
991              
992             The config to write it to.
993              
994             This is required.
995              
996             =head3 set
997              
998             This is the set name to use.
999              
1000             If not defined, the one will be choosen.
1001              
1002             =head3 revision
1003              
1004             This is the revision string to use.
1005              
1006             This is primarily meant for internal usage and is suggested
1007             that you don't touch this unless you really know what you
1008             are doing.
1009              
1010             $zconf->writeSetFromHashFile({config=>"foo/bar"}, \%hash);
1011             if($zconf->error){
1012             warn('error: '.$zconf->error.":".$zconf->errorString);
1013             }
1014              
1015             =cut
1016              
1017             #write out a config from a hash to the file backend
1018             sub writeSetFromHash{
1019 0     0 1   my $self = $_[0];
1020 0           my %args=%{$_[1]};
  0            
1021 0           my %hash = %{$_[2]};
  0            
1022              
1023 0           $self->errorblank;
1024              
1025             #return false if the config is not set
1026 0 0         if (!defined($args{config})){
1027 0           $self->{error}=25;
1028 0           $self->{errorString}='$config not defined';
1029 0           $self->warn;
1030 0           return undef;
1031             }
1032              
1033             #make sure the config name is legit
1034 0           my ($error, $errorString)=$self->{self}->configNameCheck($args{config});
1035 0 0         if(defined($error)){
1036 0           $self->{error}=$error;
1037 0           $self->{errorString}=$errorString;
1038 0           $self->warn;
1039 0           return undef;
1040             }
1041              
1042             #sets the set to default if it is not defined
1043 0 0         if (!defined($args{set})){
1044 0           $args{set}=$self->{self}->chooseSet($args{set});
1045             }else{
1046 0 0         if($self->{self}->setNameLegit($args{set})){
1047 0           $self->{args}{default}=$args{set};
1048             }else{
1049 0           $self->{error}=27;
1050 0           $self->{errorString}="'".$args{set}."' is not a legit set name.";
1051 0           $self->warn;
1052             return undef
1053 0           }
1054             }
1055            
1056             #checks to make sure the config does exist
1057 0 0         if(!$self->configExists($args{config})){
1058 0           $self->{error}=12;
1059 0           $self->{errorString}="'".$args{config}."' does not exist.";
1060 0           $self->warn;
1061 0           return undef;
1062             }
1063              
1064             #checks if it is locked or not
1065 0           my $locked=$self->isConfigLocked($args{config});
1066 0 0         if ($self->error) {
1067 0           $self->warnString('isconfigLockedFile errored');
1068 0           return undef;
1069             }
1070 0 0         if ($locked) {
1071 0           $self->{error}=45;
1072 0           $self->{errorString}='The config "'.$args{config}.'" is locked';
1073 0           $self->warn;
1074 0           return undef;
1075             }
1076            
1077             #the path to the file
1078 0           my $fullpath=$self->{args}{base}."/".$args{config}."/".$args{set};
1079              
1080             #update the revision
1081 0 0         if (!defined($args{revision})) {
1082 0           $args{revision}=time.' '.hostname.' '.rand();
1083             }
1084            
1085             #used for building it
1086 0           my $zml=ZML->new;
1087              
1088 0           my $hashkeysInt=0;#used for intering through the list of hash keys
1089             #builds the ZML object
1090 0           my @hashkeys=keys(%hash);
1091 0           while(defined($hashkeys[$hashkeysInt])){
1092             #attempts to add the variable
1093 0 0         if ($hashkeys[$hashkeysInt] =~ /^\#/) {
1094             #process a meta variable
1095 0 0         if ($hashkeys[$hashkeysInt] =~ /^\#\!/) {
1096 0           my @metakeys=keys(%{$hash{ $hashkeys[$hashkeysInt] }});
  0            
1097 0           my $metaInt=0;
1098 0           while (defined( $metakeys[$metaInt] )) {
1099 0           $zml->addMeta($hashkeys[$hashkeysInt], $metakeys[$metaInt], $hash{ $hashkeys[$hashkeysInt] }{ $metakeys[$metaInt] } );
1100             #checks to verify there was no error
1101             #this is not a fatal error... skips it if it is not legit
1102 0 0         if(defined($zml->error)){
1103 0           $self->warnString('23: $zml->addMeta() returned '.
1104             $zml->{error}.", '".$zml->{errorString}."'. Skipping variable '".
1105             $hashkeys[$hashkeysInt]."' in '".$args{config}."'.");
1106             }
1107 0           $metaInt++;
1108             }
1109             }
1110             #process a meta variable
1111 0 0         if ($hashkeys[$hashkeysInt] =~ /^\#\#/) {
1112 0           my @metakeys=keys(%{$hash{ $hashkeys[$hashkeysInt] }});
  0            
1113 0           my $metaInt=0;
1114 0           while (defined( $metakeys[$metaInt] )) {
1115 0           $zml->addComment($hashkeys[$hashkeysInt], $metakeys[$metaInt], $hash{ $hashkeys[$hashkeysInt] }{ $metakeys[$metaInt] } );
1116             #checks to verify there was no error
1117             #this is not a fatal error... skips it if it is not legit
1118 0 0         if(defined($zml->{error})){
1119 0           $self->warnString('23: $zml->addComment() returned '.
1120             $zml->{error}.", '".$zml->{errorString}."'. Skipping variable '".
1121             $hashkeys[$hashkeysInt]."' in '".$args{config}."'.");
1122             }
1123 0           $metaInt++;
1124             }
1125             }
1126             }else {
1127 0           $zml->addVar($hashkeys[$hashkeysInt], $hash{$hashkeys[$hashkeysInt]});
1128             #checks to verify there was no error
1129             #this is not a fatal error... skips it if it is not legit
1130 0 0         if(defined($zml->error)){
1131 0           $self->warnString('23: $zml->addVar returned '.
1132             $zml->{error}.", '".$zml->{errorString}."'. Skipping variable '".
1133             $hashkeys[$hashkeysInt]."' in '".$args{config}."'.");
1134             }
1135             }
1136            
1137 0           $hashkeysInt++;
1138             }
1139              
1140             #writes the config out
1141 0           $args{zml}=$zml;
1142 0           $self->writeSetFromZML(\%args);
1143 0 0         if ($self->error) {
1144 0           $self->warnString('writeSetFromZML failed');
1145             return undef
1146 0           }
1147              
1148 0           return $args{revision};
1149             }
1150              
1151             =head2 writeSetFromLoadedConfig
1152              
1153             This method writes a loaded config to a to a set,
1154             for the file backend.
1155              
1156             One arguement is required.
1157              
1158             =head2 args hash
1159              
1160             =head3 config
1161              
1162             The config to write it to.
1163              
1164             This is required.
1165              
1166             =head3 set
1167              
1168             This is the set name to use.
1169              
1170             If not defined, the one will be choosen.
1171              
1172             =head3 revision
1173              
1174             This is the revision string to use.
1175              
1176             This is primarily meant for internal usage and is suggested
1177             that you don't touch this unless you really know what you
1178             are doing.
1179              
1180             $zconf->writeSetFromLoadedConfigFile({config=>"foo/bar"});
1181             if($zconf->error){
1182             warn('error: '.$zconf->error.":".$zconf->errorString);
1183             }
1184              
1185             =cut
1186              
1187             #write a set out
1188             sub writeSetFromLoadedConfig{
1189 0     0 1   my $self = $_[0];
1190 0           my %args=%{$_[1]};
  0            
1191              
1192 0           $self->errorblank;
1193              
1194             #return false if the config is not set
1195 0 0         if (!defined($args{config})){
1196 0           $self->{error}=25;
1197 0           $self->{errorString}='$config not defined';
1198 0           $self->warn;
1199 0           return undef;
1200             }
1201              
1202 0 0         if(! $self->{self}->isConfigLoaded( $args{config} ) ){
1203 0           $self->{error}=25;
1204 0           $self->{errorString}="Config '".$args{config}."' is not loaded";
1205 0           $self->warn;
1206 0           return undef;
1207             }
1208              
1209             #checks if it is locked or not
1210 0           my $locked=$self->isConfigLocked($args{config});
1211 0 0         if ($self->{error}) {
1212 0           $self->warnString('isconfigLockedFile errored');
1213 0           return undef;
1214             }
1215 0 0         if ($locked) {
1216 0           $self->{error}=45;
1217 0           $self->{errorString}='The config "'.$args{config}.'" is locked';
1218 0           $self->warn;
1219 0           return undef;
1220             }
1221              
1222             #sets the set to default if it is not defined
1223 0 0         if (!defined($args{set})){
1224 0           $args{set}=$self->{set}{$args{config}};
1225             }else{
1226 0 0         if($self->{self}->setNameLegit($args{set})){
1227 0           $self->{args}{default}=$args{set};
1228             }else{
1229 0           $self->{error}=27;
1230 0           $self->{errorString}="'".$args{set}."' is not a legit set name.";
1231 0           $self->warn;
1232             return undef
1233 0           }
1234             }
1235              
1236             #the path to the file
1237 0           my $fullpath=$self->{args}{base}."/".$args{config}."/".$args{set};
1238              
1239             #update the revision
1240 0 0         if (!defined($args{revision})) {
1241 0           $args{revision}=time.' '.hostname.' '.rand();
1242             }
1243              
1244 0           my $zml=$self->{self}->dumpToZML($args{config});
1245 0 0         if ($self->{self}->error) {
1246 0           $self->{error}=14;
1247 0           $self->{errorString}='Failed to dump to ZML. error='.$self->{self}->error.' errorString='.$self->{self}->errorString;
1248 0           $self->warn;
1249             return undef
1250 0           }
1251 0           $args{zml}=$zml;
1252              
1253             #writes out the config
1254 0           $self->writeSetFromZML(\%args);
1255 0 0         if ($self->error) {
1256 0           $self->warnString('writeSetFromZML failed');
1257             return undef
1258 0           }
1259              
1260 0           return $args{revision};
1261             }
1262              
1263             =head2 writeSetFromZML
1264              
1265             This writes a config set from a ZML object.
1266              
1267             One arguement is required.
1268              
1269             =head2 args hash
1270              
1271             =head3 config
1272              
1273             The config to write it to.
1274              
1275             This is required.
1276              
1277             =head3 set
1278              
1279             This is the set name to use.
1280              
1281             If not defined, the one will be choosen.
1282              
1283             =head3 revision
1284              
1285             This is the revision string to use.
1286              
1287             This is primarily meant for internal usage and is suggested
1288             that you don't touch this unless you really know what you
1289             are doing.
1290              
1291             $zconf->writeSetFromZML({config=>"foo/bar", zml=>$zml});
1292             if($zconf->error){
1293             warn('error: '.$zconf->error.":".$zconf->errorString);
1294             }
1295              
1296             =cut
1297              
1298             #write a set out
1299             sub writeSetFromZML{
1300 0     0 1   my $self = $_[0];
1301 0           my %args=%{$_[1]};
  0            
1302              
1303 0           $self->errorblank;
1304              
1305             #return false if the config is not set
1306 0 0         if (!defined($args{config})){
1307 0           $self->{error}=25;
1308 0           $self->{errorString}='$config not defined';
1309 0           $self->warn;
1310 0           return undef;
1311             }
1312              
1313             #makes sure ZML is passed
1314 0 0         if (!defined( $args{zml} )) {
1315 0           $self->{error}=20;
1316 0           $self->{errorString}='$args{zml} is not defined';
1317 0           $self->warn;
1318 0           return undef;
1319             }
1320 0 0         if ( ref($args{zml}) ne "ZML" ) {
1321 0           $self->{error}=20;
1322 0           $self->{errorString}='$args{zml} is not a ZML';
1323 0           $self->warn;
1324 0           return undef;
1325             }
1326              
1327             #checks if it is locked or not
1328 0           my $locked=$self->isConfigLocked($args{config});
1329 0 0         if ($self->error) {
1330 0           $self->warnString('isconfigLockedFile errored');
1331 0           return undef;
1332             }
1333 0 0         if ($locked) {
1334 0           $self->{error}=45;
1335 0           $self->{errorString}='The config "'.$args{config}.'" is locked';
1336 0           $self->warn;
1337 0           return undef;
1338             }
1339              
1340             #sets the set to default if it is not defined
1341 0 0         if (!defined($args{set})){
1342 0           $args{set}=$self->{set}{$args{config}};
1343             }else{
1344 0 0         if($self->{self}->setNameLegit($args{set})){
1345 0           $self->{args}{default}=$args{set};
1346             }else{
1347 0           $self->{error}=27;
1348 0           $self->{errorString}="'".$args{set}."' is not a legit set name.";
1349 0           $self->warn;
1350             return undef
1351 0           }
1352             }
1353              
1354             #the path to the file
1355 0           my $fullpath=$self->{args}{base}."/".$args{config}."/".$args{set}.'.set';
1356              
1357             #update the revision
1358 0 0         if (!defined($args{revision})) {
1359 0           $args{revision}=time.' '.hostname.' '.rand();
1360             }
1361              
1362             #small hack as this was copied writeSetFromLoadedConfig
1363 0           my $zml=$args{zml};
1364              
1365             #opens the file and returns if it can not
1366             #creates it if necesary
1367 0 0         if(!open("THEFILE", '>', $fullpath)){
1368 0           $self->{error}=15;
1369 0           $self->{errorString}="'".$self->{args}{base}."/".$args{config}."/.chooser' open failed.";
1370 0           $self->warn;
1371 0           return undef;
1372             }
1373 0           print THEFILE $zml->string();
1374 0           close("THEFILE");
1375              
1376             #updates the revision
1377 0           my $revisionfile=$self->{args}{base}."/".$args{config}."/.revision";
1378 0 0         if (!defined($args{revision})) {
1379 0           $args{revision}=time.' '.hostname.' '.rand();
1380             }
1381            
1382             #opens the file and returns if it can not
1383             #creates it if necesary
1384 0 0         if(!open("THEREVISION", '>', $revisionfile)){
1385 0           $self->{error}=43;
1386 0           $self->{errorString}="'".$revisionfile."' open failed";
1387 0           $self->warn;
1388 0           return undef;
1389             }
1390 0           print THEREVISION $args{revision};
1391 0           close("THEREVISION");
1392             #save the revision info
1393 0           $self->{self}->{revision}{$args{config}}=$args{revision};
1394              
1395 0           return $args{revision};
1396             }
1397              
1398             =head1 ERROR HANDLING/CODES
1399              
1400             This module uses L for error handling. Below are the
1401             error codes returned by the error method.
1402              
1403             =head2 1
1404              
1405             config name contains ,
1406              
1407             =head2 2
1408              
1409             config name contains /.
1410              
1411             =head2 3
1412              
1413             config name contains //
1414              
1415             =head2 4
1416              
1417             config name contains ../
1418              
1419             =head2 5
1420              
1421             config name contains /..
1422              
1423             =head2 6
1424              
1425             config name contains ^./
1426              
1427             =head2 7
1428              
1429             config name ends in /
1430              
1431             =head2 8
1432              
1433             config name starts with /
1434              
1435             =head2 9
1436              
1437             could not sync to file
1438              
1439             =head2 10
1440              
1441             config name contains a \n
1442              
1443             =head2 11
1444              
1445             ZML dump failed.
1446              
1447             =head2 12
1448              
1449             config does not exist
1450              
1451             =head2 14
1452              
1453             file/dir does not exist
1454              
1455             =head2 15
1456              
1457             file/dir open failed
1458              
1459             =head2 16
1460              
1461             file/dir creation failed
1462              
1463             =head2 17
1464              
1465             file write failed
1466              
1467             =head2 18
1468              
1469             No variable name specified.
1470              
1471             =head2 19
1472              
1473             config key starts with a ' '
1474              
1475             =head2 20
1476              
1477             ZML object not specified.
1478              
1479             =head2 21
1480              
1481             set not found for config
1482              
1483             =head2 22
1484              
1485             LDAPmakepathSimple failed
1486              
1487             =head2 23
1488              
1489             skilling variable as it is not a legit name
1490              
1491             =head2 24
1492              
1493             set is not defined
1494              
1495             =head2 25
1496              
1497             Config is undefined.
1498              
1499             =head2 26
1500              
1501             Config not loaded.
1502              
1503             =head2 27
1504              
1505             Set name is not a legit name.
1506              
1507             =head2 28
1508              
1509             ZML->parse error.
1510              
1511             =head2 29
1512              
1513             Could not unlink the unlink the set.
1514              
1515             =head2 30
1516              
1517             The sets exist for the specified config.
1518              
1519             =head2 31
1520              
1521             Did not find a matching set.
1522              
1523             =head2 32
1524              
1525             Unable to choose a set.
1526              
1527             =head2 33
1528              
1529             Unable to remove the config as it has sub configs.
1530              
1531             =head2 34
1532              
1533             LDAP connection error
1534              
1535             =head2 35
1536              
1537             Can't use system mode and file together.
1538              
1539             =head2 36
1540              
1541             Could not create '/var/db/zconf'. This is a permanent error.
1542              
1543             =head2 37
1544              
1545             Could not create '/var/db/zconf/'. This is a permanent error.
1546              
1547             =head2 38
1548              
1549             Sys name matched /\//.
1550              
1551             =head2 39
1552              
1553             Sys name matched /\./.
1554              
1555             =head2 40
1556              
1557             No chooser string specified.
1558              
1559             =head2 41
1560              
1561             No comment specified.
1562              
1563             =head2 42
1564              
1565             No meta specified.
1566              
1567             =head2 43
1568              
1569             Failed to open the revision file for the set.
1570              
1571             =head2 44
1572              
1573             Failed to open or unlink lock file.
1574              
1575             =head2 45
1576              
1577             Config is locked.
1578              
1579             =head2 46
1580              
1581             The base does not exist or could not be created.
1582              
1583             =head2 47
1584              
1585             No ZConf object passed.
1586              
1587             =head2 48
1588              
1589             No zconf.zml var hash passed.
1590              
1591             =head1 ERROR CHECKING
1592              
1593             This can be done by checking $zconf->{error} to see if it is defined. If it is defined,
1594             The number it contains is the corresponding error code. A description of the error can also
1595             be found in $zconf->{errorString}, which is set to "" when there is no error.
1596              
1597             =head1 zconf.zml
1598              
1599             The default is 'xdf_config_home/zconf.zml', which is generally '~/.config/zconf.zml'. See perldoc
1600             ZML for more information on the file format. The keys are listed below.
1601              
1602             =head2 keys
1603              
1604             =head3 backend
1605              
1606             This should be set to 'file' to use this backend.
1607              
1608             =head3 fileonly
1609              
1610             This is a boolean value. If it is set to 1, only the file backend is used.
1611              
1612             This will override 'backend'.
1613              
1614             Basically the same as using the backend to 'file'.
1615              
1616             =head3 file/base
1617              
1618             This is the base directory to use for storing the configs in.
1619              
1620             =head1 AUTHOR
1621              
1622             Zane C. Bowers-Hadley, C<< >>
1623              
1624             =head1 BUGS
1625              
1626             Please report any bugs or feature requests to C, or through
1627             the web interface at L. I will be notified, and then you'll
1628             automatically be notified of progress on your bug as I make changes.
1629              
1630             =head1 SUPPORT
1631              
1632             You can find documentation for this module with the perldoc command.
1633              
1634             perldoc ZConf
1635              
1636              
1637             You can also look for information at:
1638              
1639             =over 4
1640              
1641             =item * RT: CPAN's request tracker
1642              
1643             L
1644              
1645             =item * AnnoCPAN: Annotated CPAN documentation
1646              
1647             L
1648              
1649             =item * CPAN Ratings
1650              
1651             L
1652              
1653             =item * Search CPAN
1654              
1655             L
1656              
1657             =item * Subversion Repository
1658              
1659             L
1660              
1661             =back
1662              
1663             =head1 ACKNOWLEDGEMENTS
1664              
1665             =head1 COPYRIGHT & LICENSE
1666              
1667             Copyright 2011 Zane C. Bowers-Hadley, all rights reserved.
1668              
1669             This program is free software; you can redistribute it and/or modify it
1670             under the same terms as Perl itself.
1671              
1672             =cut
1673              
1674             1; # End of ZConf