line  
 stmt  
 bran  
 cond  
 sub  
 pod  
 time  
 code  
 
1 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #!/usr/bin/perl  
 
2 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
3 
 
 
 
 
 
 
 
 
 
 
 
 
 
 package Config::Cascade;  
 
4 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
5 
 
1
 
 
 
 
 
  
1
   
 
 
 
24331
 
 use warnings;  
 
  
 
1
 
 
 
 
 
 
 
 
 
2
 
    
 
  
 
1
 
 
 
 
 
 
 
 
 
65
 
    
 
6 
 
1
 
 
 
 
 
  
1
   
 
 
 
6
 
 use strict;  
 
  
 
1
 
 
 
 
 
 
 
 
 
2
 
    
 
  
 
1
 
 
 
 
 
 
 
 
 
35
 
    
 
7 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
8 
 
1
 
 
 
 
 
  
1
   
 
 
 
874
 
 use Regexp::Common;  
 
  
 
1
 
 
 
 
 
 
 
 
 
5657
 
    
 
  
 
1
 
 
 
 
 
 
 
 
 
5
 
    
 
9 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
10 
 
 
 
 
 
 
 
 
 
 
 
 
 
 my %Options;  
 
11 
 
 
 
 
 
 
 
 
 
 
 
 
 
 my %configValidation;  
 
12 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
13 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 NAME  
 
14 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
15 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Config::Cascade - simple configuration file framework for managing multi-level configurations, with regexp validation.  
 
16 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
17 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 VERSION  
 
18 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
19 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Version 0.02  
 
20 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
21 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
22 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
23 
 
 
 
 
 
 
 
 
 
 
 
 
 
 our $VERSION = '0.02';  
 
24 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
25 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 SYNOPSIS  
 
26 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
27 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Config::Cascade is intended to allow the use of global configurations in combination with more specific  
 
28 
 
 
 
 
 
 
 
 
 
 
 
 
 
 configs, with the added benefit of overriding all of these settings from the command line. This benefit   
 
29 
 
 
 
 
 
 
 
 
 
 
 
 
 
 allows the use of a standard base config, with a simple format, while allowing custom configs for multiple  
 
30 
 
 
 
 
 
 
 
 
 
 
 
 
 
 programs utilizing the same resources or configurations. Validated configuration options will function in both global   
 
31 
 
 
 
 
 
 
 
 
 
 
 
 
 
 and specific configuration files, and may also be referenced from the command line using Getopt::Long and getopt style  
 
32 
 
 
 
 
 
 
 
 
 
 
 
 
 
 notation:  
 
33 
 
 
 
 
 
 
 
 
 
 
 
 
 
  --= or -=      
 
34 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
35 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Example:  
 
36 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
37 
 
 
 
 
 
 
 
 
 
 
 
 
 
     use Config::Cascade;  
 
38 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my %config = Config::Cascade->new(    
 
39 
 
 
 
 
 
 
 
 
 
 
 
 
 
 					configDir => '/etc/Frood',   
 
40 
 
 
 
 
 
 
 
 
 
 
 
 
 
 					globalConfig => 'global.cfg',   
 
41 
 
 
 
 
 
 
 
 
 
 
 
 
 
 					configFile => 'example.cfg',  
 
42 
 
 
 
 
 
 
 
 
 
 
 
 
 
 					validate => {  
 
43 
 
 
 
 
 
 
 
 
 
 
 
 
 
 						host => {type => 'fqdn'},  
 
44 
 
 
 
 
 
 
 
 
 
 
 
 
 
 						port => {type => 'regexp', args=> 'RE::num::int'},  
 
45 
 
 
 
 
 
 
 
 
 
 
 
 
 
 						url => {type => 'regexp', args => '^http:'},  
 
46 
 
 
 
 
 
 
 
 
 
 
 
 
 
 						},  
 
47 
 
 
 
 
 
 
 
 
 
 
 
 
 
  				     );  
 
48 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
49 
 
 
 
 
 
 
 
 
 
 
 
 
 
     print $config{host};  
 
50 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
51 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 Validation File format  
 
52 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
53 
 
 
 
 
 
 
 
 
 
 
 
 
 
 A validation file follows a simple format, one entry per line:  
 
54 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
55 
 
 
 
 
 
 
 
 
 
 
 
 
 
        
 
56 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
57 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Config variable names are arbitrary, but must be single strings, sans white space. The following is a list of valid data types.  
 
58 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
59 
 
 
 
 
 
 
 
 
 
 
 
 
 
 alias  - Declares this entry to be an alias for another. Alias requires an optional argument referring to the parent option.  
 
60 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
61 
 
 
 
 
 
 
 
 
 
 
 
 
 
 bool   - Sets the value to '1' if present.  
 
62 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
63 
 
 
 
 
 
 
 
 
 
 
 
 
 
 int    - Matches the equiv of $RE{num}{int}  
 
64 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
65 
 
 
 
 
 
 
 
 
 
 
 
 
 
 fqdn   - Matches fully qualified domain names for formatting, using $RE{net}{domain}  
 
66 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
67 
 
 
 
 
 
 
 
 
 
 
 
 
 
 regexp - Matches a free form regular expression, or refers to an entry in Regexp::Common. This requires an optional argument of a valid regular expression, or RE reference. Specifying 'RE::' informs the parser a Regexp::Common regexp is being invoked, with subsequent delimited entries corresponding to Regexp::Common's multi-level hash syntax. Otherwise, the contents of args will be precompiled as-is and matched accordingly.  
 
68 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
69 
 
 
 
 
 
 
 
 
 
 
 
 
 
 string - Matches the equiv of /\w+/  
 
70 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
71 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 Config File format  
 
72 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
73 
 
 
 
 
 
 
 
 
 
 
 
 
 
 A configuration file follows a simple format, one entry per line:  
 
74 
 
 
 
 
 
 
 
 
 
 
 
 
 
      
 
75 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
76 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 FUNCTIONS  
 
77 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
78 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head2 new  
 
79 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
80 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	Performs start-up sanity checks and functions. The module will at first attempt to load a   
 
81 
 
 
 
 
 
 
 
 
 
 
 
 
 
 global configuration file, if available. After that, a more specific configuration file (if available),  
 
82 
 
 
 
 
 
 
 
 
 
 
 
 
 
 is loaded, overriding any settings in the global configuration that collide. After that, command line  
 
83 
 
 
 
 
 
 
 
 
 
 
 
 
 
 options then override any loaded settings that collide.  
 
84 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
85 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	Valid options:  
 
86 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		configDir - Specifies a directory containing configuration files.  
 
87 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			- If not specified, will use the current working directory.  
 
88 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		configFile - Specifies a specific configuration file to be read.  
 
89 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			- If not specifed, will be ignored.  
 
90 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		debug - enables debug output.  
 
91 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		globalConfig - Specifies a global config shared between multiple programs.  
 
92 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			- If not specified, global.cfg will be looked for in the specified configDir.  
 
93 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		noCommandLine - Skips parsing of @ARGV.  
 
94 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		noConfig - Skips reading of configuration files. Command line options will be used.  
 
95 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		noValidation - Skips use of validation functions entirely.   
 
96 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		validate - Optional hash structure containing validation instructions.  
 
97 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		validationFile - Specifies a file containing configuration validation instructions.  
 
98 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			- If not specified, global.validation will be looked for in the specified configDir.  
 
99 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
100 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
101 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
102 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub new {   
 
103 
 
0
 
 
 
 
 
  
0
   
 
  
1
   
 
 
 
 	my ($class, $hashref) = @_;  
 
104 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
105 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	%Options = %{$hashref};  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
106 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my %running;						# The current config hash, as it passes from phase to phase.  
 
107 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	my %global;						# Application specific config  
 
108 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my %specific;						# Application specific config  
 
109 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my %commandLine;					# Command line options  
 
110 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
111 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	$Options{configDir} or $Options{configDir} = '.';	# No configDir? Use pwd.  
 
112 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
113 
 
0
 
  
  0
   
 
  
  0
   
 
 
 
 
 
 
 
 	$Options{globalConfigFile} = 'global.cfg' unless ($Options{globalConfigFile} || $Options{noConfig});  
 
114 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
115 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Build validation base in preparation for reading and validation of config files and command line  
 
116 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	unless($Options{noValidation}) {  
 
117 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		if($Options{validation}) { %configValidation = %{ $Options{validation} }}  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
118 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		else { %configValidation = loadDefaultValidation() }  
 
119 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
120 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		constructValidation() or die "Errors found in validation structure, aborting.\n";  
 
121 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
122 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
123 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	unless($Options{noConfig}) {  
 
124 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		die "Specified configDir ($Options{configDir}) doesn't appear to be a directory.\n" unless (-d $Options{configDir});  
 
125 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		die "Specified configFile doesn't appear to exist." unless ( -e "$Options{configDir}/$Options{configFile}");  
 
126 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
127 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Global config has no failure check, because it's optional.  
 
128 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
129 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Read global options  
 
130 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		%global = loadConfigFile($Options{configDir} . '/' . $Options{globalConfigFile} );  
 
131 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		unless ($Options{noValidation}) {  
 
132 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			validateConfig(%global) or die "Global config failed validation.";  
 
133 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
134 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	  
 
135 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Read process specific options  
 
136 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		%specific = loadConfigFile($Options{configDir} . '/' . $Options{configFile});  
 
137 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		unless ($Options{noValidation}) {  
 
138 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			validateConfig(%specific) or die "Config failed validation.";  
 
139 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
140 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
141 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		%running = %global;  
 
142 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
143 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
144 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
145 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	unless($Options{noCommandLine}) {  
 
146 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		my %commandLine = parseCommandLine(@main::ARGV);  
 
147 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		unless ($Options{noValidation}) {  
 
148 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			validateConfig(%specific) or die "Config failed validation.";  
 
149 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
150 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
151 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Override with command line options  
 
152 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		if($Options{debug}) {  
 
153 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			foreach my $key (sort keys %commandLine) { warn "Command Line: $key: $commandLine{$key}\n"; }  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
154 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
155 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
156 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
157 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
158 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	foreach my $key ( keys %specific ) {  
 
159 
 
0
 
  
  0
   
 
  
  0
   
 
 
 
 
 
 
 
 		if(exists $global{$key} && $Options{debug}) {  
 
160 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			warn "Specific option $key overriding global value ($running{$key}) with:$specific{$key}\n";  
 
161 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
162 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		  
 
163 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		$running{$key} = $specific{$key};  
 
164 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
165 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
166 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	foreach my $key ( keys %commandLine ) {  
 
167 
 
0
 
  
  0
   
 
  
  0
   
 
 
 
 
 
 
 
 		if($global{$key} && $Options{debug}) {  
 
168 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			warn "Specific option $key overriding global value ($running{$key}) with:$commandLine{$key}\n";  
 
169 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
170 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		  
 
171 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		$running{$key} = $commandLine{$key};  
 
172 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
173 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
174 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	return(%running);  
 
175 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
176 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
177 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub loadConfigFile {  
 
178 
 
0
 
 
 
 
 
  
0
   
 
  
0
   
 
 
 
         my $target = shift;  
 
179 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my %hash; local *IN;  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
180 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
181 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
         open(IN, $target) or die "Error opening config file ($target): $!\n";   
 
182 
 
0
 
 
 
 
 
 
 
 
 
 
 
         while() {   
 
183 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		chomp;  
 
184 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		my $line = $_; $line =~ s/^\s+//;  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
185 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		next if $line eq '';  
 
186 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		my ($command, $opt) = $line =~ /^(\w+)\s*(.*)/;  
 
187 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
188 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Check and expand aliases  
 
189 
 
0
 
  
  0
   
 
  
  0
   
 
 
 
 
 
 
 
 		if(!$Options{noValidation} && $configValidation{$command}{type} eq 'alias') {  
 
190 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			$command = $configValidation{$command}{arg};  
 
191 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		  
 
192 
 
0
 
  
  0
   
 
  
  0
   
 
 
 
 
 
 
 
 			if($hash{$command} && $hash{$command} ne $opt) {	# Alias expansion collision  
 
193 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				warn "Alias expansion for $command has resulted in a collision, skipping alias\n";  
 
194 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
195 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
196 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		else { $hash{$command} = $opt; }  
 
197 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
198 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
                 warn "Config($target): $command = $opt\n" if $Options{debug};  
 
199 
 
 
 
 
 
 
 
 
 
 
 
 
 
         }  
 
200 
 
0
 
 
 
 
 
 
 
 
 
 
 
         close(IN);  
 
201 
 
0
 
 
 
 
 
 
 
 
 
 
 
         return %hash;  
 
202 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
203 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
204 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub loadDefaultValidation {  
 
205 
 
0
 
 
 
 
 
  
0
   
 
  
0
   
 
 
 
 	my($dir, $target);  
 
206 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
207 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	$dir = $Options{configDir};  
 
208 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	$target = $Options{validationFile} or $target = 'global.validation';		# Set default if needed  
 
209 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
210 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	return(0) unless ( -e "$dir/$target" ); # Fail quietly if it doesn't exist, as it may not be in use  
 
211 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
212 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my %hash; local *IN;  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
213 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
214 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 	if(open(IN, "$dir/$target")) {  
 
215 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		my $count = 0;  
 
216 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
217 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		while() {   
 
218 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			$count++;  
 
219 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			chomp;  
 
220 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			my $line = $_;  
 
221 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
222 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			$line =~ s/(.*)\#.*$/$1/; # Strip anything resembling a comment because that's only used by the humans  
 
223 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			next if $line eq '';	  # Skip the line if we just reduced it to nothing  
 
224 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
225 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if( my ($option, $format, $arg) = $line =~ /^(\w+)\s+(string|int|alias|fqdn|bool|regexp)\s*(.*)/i ) {   
 
226 
 
 
 
 
 
 
 
 
 
 
 
 
 
 												# Check for valid format  
 
227 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				$hash{$option}{type} = $format;  
 
228 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				$hash{$option}{arg} = $arg;  
 
229 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
230 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			else {   
 
231 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				warn "$dir/$target: Invalid format on line $count, ignoring: $line\n";   
 
232 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
233 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
234 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		close(IN);  
 
235 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		return(%hash);  
 
236 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
237 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	else {   
 
238 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		warn "Unable to read $dir/$target: $!\n";   
 
239 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		return(0);   
 
240 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
241 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
242 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
243 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub constructValidation {  
 
244 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
245 
 
0
 
 
 
 
 
  
0
   
 
  
0
   
 
 
 
 	my $regexp;   
 
246 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
247 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Run through and expand Regexp::Common references  
 
248 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
249 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# A command option that is intended to be validated by a R::C regexp is annotated with the following syntax:  
 
250 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	#  regexp RE::X::Y   
 
251 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Example:   
 
252 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# 	port regexp RE::num::int  
 
253 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	#	fqdn regexp RE::net::domain  
 
254 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
255 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Users also may specify custom regexps which are simply tested for syntax and precompiled.  
 
256 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	#  regexp     
 
257 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Example:  
 
258 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# url regexp ^http://.*[\s*|$]   
 
259 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
260 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my $success = 1;	# Failures set success to 0 and keep processing, to report the most errors   
 
261 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				# possible before bailing out.  
 
262 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
263 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	foreach my $option (keys %configValidation) {  
 
264 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		if($configValidation{$option}{type} eq 'regexp' ) {  
 
265 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		   	if ($configValidation{$option}{arg} =~ /^RE::(.*)\s*/) {  
 
266 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				# Translate :: delimiter for search into $RE{}{} multi level hash structure.  
 
267 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				my @levels = split /::/, $1;   
 
268 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				$regexp = \%RE;   
 
269 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				for my $REid (@levels) {			# Thanks to bline for an elegant solution  
 
270 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 					if ( defined $regexp->{$REid} ) { $regexp = $regexp->{$REid}; }  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
271 
 
 
 
 
 
 
 
 
 
 
 
 
 
 					else {  
 
272 
 
0
 
 
 
 
 
 
 
 
 
 
 
         					warn "Invalidating directive '$option': Regexp $REid does not exist within Regexp::Common";  
 
273 
 
0
 
 
 
 
 
 
 
 
 
 
 
 						$success = 0;	# Error in the validation, treat it as a failure and block continued loading  
 
274 
 
 
 
 
 
 
 
 
 
 
 
 
 
 					}  
 
275 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				};  
 
276 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
277 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			else {  
 
278 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 				unless( eval{ qr($configValidation{$option}{arg}) } ) {  
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
279 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					warn "Invalidating directive '$option': Compiling regexp ($configValidation{$option}{arg}) returned errors: $@\n";	  
 
280 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					$success = 0;	# Error in the validation, treat it as a failure and block continued loading  
 
281 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				}  
 
282 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
283 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
284 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
285 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	return($success);  
 
286 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
287 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
288 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub validateConfig {  
 
289 
 
0
 
 
 
 
 
  
0
   
 
  
0
   
 
 
 
 	my %hash = @_;  
 
290 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my $success = 1;  
 
291 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
292 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my @validate = keys %hash;  
 
293 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	foreach my $key (@validate) {  
 
294 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		if($configValidation{$key}) {  
 
295 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# Expand if alias.  
 
296 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if($configValidation{$key}{type} eq 'alias') {   
 
297 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				push(@validate, $configValidation{$key}{args});	# Expand the alias, requeue for testing  
 
298 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				next;  
 
299 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
300 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
301 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# bool  
 
302 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if($configValidation{$key}{type} eq 'bool') {  
 
303 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				# Honestly, there's nothing to do with these, it's there or it isn't!  
 
304 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				next;  
 
305 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
306 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
307 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# int  
 
308 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if($configValidation{$key}{type} eq 'int') {		# int is just a shortcut to $RE{num}{int}  
 
309 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 				unless ($hash{$key} =~ /$RE{num}{int}/) {  
 
310 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					warn "$key is not an integer: $hash{$key}\n";  
 
311 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					$success = 0;  
 
312 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				}  
 
313 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				next;  
 
314 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
315 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
316 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# string  
 
317 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if($configValidation{$key}{type} eq 'string') {  
 
318 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 				unless ($hash{$key} =~ /\w+/) {  
 
319 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					warn "$key is not an string: $hash{$key}\n";  
 
320 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					$success = 0;  
 
321 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				}  
 
322 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				next;  
 
323 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
324 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
325 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# fqdn  
 
326 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if($configValidation{$key}{type} eq 'fqdn') {  
 
327 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 				unless ($hash{$key} =~ /$RE{net}{domain}/) {  
 
328 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					warn "$key is not an fqnd: $hash{$key}\n";  
 
329 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					$success = 0;  
 
330 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				}  
 
331 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				next;  
 
332 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
333 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
334 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# regexp  
 
335 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 			if($configValidation{$key}{type} eq 'regexp') {  
 
336 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 				unless ($hash{$key} =~ /$configValidation{$key}{args}/) {  
 
337 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					warn "$key does not match regexp: $hash{$key}\n";  
 
338 
 
0
 
 
 
 
 
 
 
 
 
 
 
 					$success = 0;  
 
339 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				}  
 
340 
 
0
 
 
 
 
 
 
 
 
 
 
 
 				next;  
 
341 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
342 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			warn "$key has a type of $configValidation{$key}{type}, which is unrecognized.\n";  
 
343 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			$success = 0;  
 
344 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
345 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		else {  
 
346 
 
0
 
 
 
 
 
 
 
 
 
 
 
 			warn "Invalid config option (not declared in validation): $key \n";  
 
347 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
348 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}	  
 
349 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	return($success);  
 
350 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
351 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
352 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub parseCommandLine  {  
 
353 
 
0
 
 
 
 
 
  
0
   
 
  
0
   
 
 
 
 	my @options = @_;   
 
354 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	my %hash;  
 
355 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
356 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	while(@options) {  
 
357 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		my $arg = shift(@options);  
 
358 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
359 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Quoted string checks need to go here.  
 
360 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# As soon as I learn how to do them right.  
 
361 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
362 
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
 		if($arg =~ /^--(\w+)=(.*)/) { $hash{$1} = $2; }  
 
  
 
0
 
  
  0
   
 
 
 
 
 
 
 
 
 
    
 
363 
 
0
 
 
 
 
 
 
 
 
 
 
 
 		elsif ($arg =~ /^-(\w)=(.*)/) { $hash{$1} = $2; }  
 
364 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
365 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
366 
 
0
 
 
 
 
 
 
 
 
 
 
 
 	return(%hash);  
 
367 
 
 
 
 
 
 
 
 
 
 
 
 
 
 };  
 
368 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
369 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 AUTHOR  
 
370 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
371 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Bill Nash, C<<  >>   
 
372 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
373 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 ACKNOWLEDGEMENTS  
 
374 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
375 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Thanks go to bline, dngor, Somni, the letter P, and the number 2.  
 
376 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
377 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 COPYRIGHT & LICENSE  
 
378 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
379 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Copyright 2005 Bill Nash, All Rights Reserved.  
 
380 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
381 
 
 
 
 
 
 
 
 
 
 
 
 
 
 This program is free software; you can redistribute it and/or modify it  
 
382 
 
 
 
 
 
 
 
 
 
 
 
 
 
 under the same terms as Perl itself.  
 
383 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
384 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
385 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
386 
 
 
 
 
 
 
 
 
 
 
 
 
 
 1; # End of Config::Cascade