line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################## |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# MyConfig Configuration File Parser Library |
4
|
|
|
|
|
|
|
# Written by Markus Guertler |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
############################################## |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# This package reads and parses configuration files in 'Apache Style' with directives |
10
|
|
|
|
|
|
|
# and returns a hash-tree of the configuration |
11
|
|
|
|
|
|
|
# See the perlpod manual or the example for more information. |
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# by Markus |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Config::MyConfig2 is a flexible configuration file parser, that reads and writes |
19
|
|
|
|
|
|
|
Apache-Style configuration files, with global key/value pairs and |
20
|
|
|
|
|
|
|
directives |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
It supports: |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=over 4 |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=item * Configureable layout of configuration files, i.e. which keywords, which directives (if any), syntax checks for values |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=item * Flexible configurations, i.e. using tabs, spaces or = as delimiters between keywords and values |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=item * Apache Webserver style configuration directives: keywords & values |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=item * Keywords with multiple values, either as comma seperated list or as multiple keywords with the same name |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=item * Methods to gather loaded configuration values in Perl context, i.e. as hashtree, lists or single values |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=item * Ability to modify the configuration, after it has been loaded |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=item * Ability to store a modified configuration file back to disk |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=item * Full Perl OO access |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=back |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 SYNOPSIS |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
my $myconfig = Config::MyConfig2->new ( |
47
|
|
|
|
|
|
|
conffile => "my_configuration_file.cfg", |
48
|
|
|
|
|
|
|
); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
my $conftemplate; |
51
|
|
|
|
|
|
|
$conftemplate->{global}->{somenumber} = { required => 'true', type => 'single', match => '^\d+\.*\d*'}; |
52
|
|
|
|
|
|
|
$conftemplate->{global}->{somestring} = { required => 'false', type => 'single', match => '^.+'}; |
53
|
|
|
|
|
|
|
$conftemplate->{directive}->{foo} = { type => 'single', match => '^[true]|[false]$'}; |
54
|
|
|
|
|
|
|
$conftemplate->{directive}->{bar} = { type => 'single', match => '^0|1$'}; |
55
|
|
|
|
|
|
|
$conftemplate->{other_directive}->{far} = { type => 'list', match => '.+'}; |
56
|
|
|
|
|
|
|
$conftemplate->{other_directive}->{boo} = { type => 'list', match => '.+'}; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
$myconfig->SetupDirectives($conftemplate); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $config_hashtree = $myconfig->ReadConfig(); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $global_value = $myconfig->GetGlobalValue('foo'); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
$errmsg = $myconfig->SetDirectiveValue('directive_foo','identifier_baz','key_foobar','value_foo_bar_baz'); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
$myconfig->WriteConfig('My new config file','some_file.cfg'); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 DESCRIPTION |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
This class provides methods to setup a configuration file template as well as |
72
|
|
|
|
|
|
|
to read and parse a configuration file, that matches to the template. The |
73
|
|
|
|
|
|
|
configuration can have Apache-Configuration style directives. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Furthermore, an existing configuration can be modified and written back to disk. |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
It supports... |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=over |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item * Global keywords |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
keyword foo |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item * keywords with lists in CSV (comma separated value) format |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
keyword foo, bar, boo, far |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item * Directives with names and user-defined identifiers: |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
keyword foo |
93
|
|
|
|
|
|
|
other_keyword bar |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
perl_program hello_world.pl |
98
|
|
|
|
|
|
|
argument foobar |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=back |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 METHODS |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 new |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
Creates a new Config::MyConfig2 object |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
my $myconfig = Config::MyConfig2->new ( |
110
|
|
|
|
|
|
|
conffile => "my_configuration_file.cfg", |
111
|
|
|
|
|
|
|
); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head2 SetupDirectives |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$myconfig->SetupDirectives($conftemplate); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
Where $conftemplate is a hash tree data structure. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=over 2 |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item Global Values |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Global values are key/value pairs, that are not living in a directive. This |
124
|
|
|
|
|
|
|
can be i.e. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
animal = cow |
127
|
|
|
|
|
|
|
or |
128
|
|
|
|
|
|
|
animal cow |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
and would be templated like this: |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
$tmpl->{global}->{animal} = { match => '.+', type => 'single'} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Allowed delimiters are spaces, tabs and = |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item Directive Values |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Directive values are values, that are living within a directive. Each diretive |
139
|
|
|
|
|
|
|
has a name and an identifier, i.e. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
bar 100 |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
The identifiers can freely be choosen by the user. The directive names are |
146
|
|
|
|
|
|
|
predifined in the template. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
$tmpl->{my_directive}->{bar} = {match => '.+', type = 'single'} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
The keyword 'bar' would match for all directive name / directive identifier combinations |
151
|
|
|
|
|
|
|
with the directive 'name my_directive'. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item Keyword Types |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Keyword types can be: |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=over 4 |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item single |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
A single item can only be defined once and appears as a scalar in the config |
162
|
|
|
|
|
|
|
hash tree. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
foo bar |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
If gathered via GetGlobalValue or GetDirectiveValue, these items will be returned as an |
167
|
|
|
|
|
|
|
array reference. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item multi |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
A multi item can be defined multiple times, either as a list of repeated keyword / value pairs |
172
|
|
|
|
|
|
|
or as a comma seperated list of values with one keyword |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
foo = 1 |
175
|
|
|
|
|
|
|
foo = 2 |
176
|
|
|
|
|
|
|
foo = 3 |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
or |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
foo = 1, 2, 3 |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
or, of course, something like this: |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
foo 1 ,2, 3 |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
If gathered via GetGlobalValue or GetDirectiveValue, these items will be returned as an |
187
|
|
|
|
|
|
|
array reference. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item Syntax Check / Match Operator |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
The match operator is a regex, where a supplied value in the configuration file is checked against. This enables |
194
|
|
|
|
|
|
|
the possibility of syntax checking configuration parameters. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
If a check fails, an errors is thrown. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=back |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head2 ReadConfig |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$config_hash_tree = $self->ReadConfig() |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Reads and parses the configuration file. Throws an error, if a parsing error (i.e. syntax error) occurs. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Returns the configuration as a hash_tree. See the example below. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=head2 GetDirectiveNames |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Returns a list of all directive names as an array or an empty list, if no directive names have been found. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
@directives = $myconfig->GetDirectiveNames() |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=head2 GetDirectiveIdentifiers |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Expects the name of a pre-defined directive |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
Returns a list of all directive identifiers as an array or an empty list in case of identifiers have been found. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
@identifiers = $myconfig->GetDirectiveIdentifiers('my_directive') |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head2 GetConfigRef |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Returns a hash reference to the configuration, which is a nested datastructure. You might want to use |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
use Data::Dumper; |
227
|
|
|
|
|
|
|
print Dumper($config_reference) |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
to evaluate the details of this structure. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Because it is a reference, all modifications of this structure will also end up in configuration files, written |
232
|
|
|
|
|
|
|
with WriteConfig(). |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 GetGlobalValue |
235
|
|
|
|
|
|
|
Expects the name of a valid keyword |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
Returns a global value as a scalar (type = single) or a reference to an array |
238
|
|
|
|
|
|
|
(type = multi) |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
$value = $myconfig->GetGlobalValue('foo') |
241
|
|
|
|
|
|
|
$value_array_ref = $myconfig->GetGlobalValue('foo') |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head2 GetDirectiveName |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
Expects the name of a directive and a keyword |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Returns a global value as a scalar (type = single) or a reference to an array |
248
|
|
|
|
|
|
|
(type = multi) |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
$value = $myconfig->GetGlobalValue('my_directive','foo') |
251
|
|
|
|
|
|
|
$value_array_ref = $myconfig->GetGlobalValue('my_directive','foo') |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=head2 GetDirectiveValue |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Expects the name of a directive, directive identifier and a keyword. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
Returns a directive value as a scalar (type = single) or a reference to an array |
258
|
|
|
|
|
|
|
(type = multi) |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
$value = $myconfig->GetGlobalValue('my_directive','some_identifier','foo') |
261
|
|
|
|
|
|
|
$value_array_ref = $myconfig->GetGlobalValue('foo') |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head2 SetGlobalValue |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Sets the value of a global keyword. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Expects a pre-defined global keyword and a value |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Returns undef in case of success or an string with a error message. It uses the |
270
|
|
|
|
|
|
|
syntax-checker to verifiy if the global value meets the requirements of the |
271
|
|
|
|
|
|
|
checkng regex. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
$errmsg = $myconfig->SetGlobalValue('some_keyword','some_value') |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
If the keyword is of type 'multi', the passed value will be added to a list of values. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head2 SetDirectiveValue |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
Sets the value of a keyword within a directive. |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Expects a directive-name, directive identifier, keyword and a value. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Returns undef in case of success or an string with a error message. It uses the |
284
|
|
|
|
|
|
|
syntax-checker to verifiy if the global value meets the requirements of the |
285
|
|
|
|
|
|
|
checkng regex. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
$errmsg = $myconfig->SetGlobalValue('some_directive','some_identifier','some_keyword','some_value') |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
If the directive identifier doesn't exist, it will be created. If the keyword is of type 'multi', the |
290
|
|
|
|
|
|
|
passed value will be added to a list of values. |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=head2 DeleteDirectiveIdentifier |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Deletes an identifier from a directive. |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Expects a directive name and directive identifier |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
Returns the removed values or undef is no values for this directive/identifiehave been deleted. |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=head2 WriteConfig |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Writes the (modified) configuration file back to disk. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Expects a name-string, that is shown in the configuration file header comments and a filename where |
305
|
|
|
|
|
|
|
the configuration should be saved to. |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
$myconfig->WriteConfig('Foo Bars Configuration File','/tmp/foo.cfg'); |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=head2 error |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
Internal method, that is used to throw an error. The default behavior is to |
312
|
|
|
|
|
|
|
croack(). |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head1 EXAMPLE |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=over |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=item * Configuration file for a backup script: backup.cfg |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
--- snip --- |
321
|
|
|
|
|
|
|
# |
322
|
|
|
|
|
|
|
# Config file |
323
|
|
|
|
|
|
|
# |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# |
326
|
|
|
|
|
|
|
# ---- Global Section ---- |
327
|
|
|
|
|
|
|
# |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# Path to the rsync programm |
330
|
|
|
|
|
|
|
rsync /usr/bin/rsync |
331
|
|
|
|
|
|
|
# Path to sendmail |
332
|
|
|
|
|
|
|
sendmail /usr/sbin/sendmail |
333
|
|
|
|
|
|
|
# Path to the tar utility |
334
|
|
|
|
|
|
|
tar /bin/tar |
335
|
|
|
|
|
|
|
# Path to ssh command |
336
|
|
|
|
|
|
|
# If not specified, rsh will be used |
337
|
|
|
|
|
|
|
ssh /usr/bin/ssh |
338
|
|
|
|
|
|
|
# Debuglevel, range (0..2) |
339
|
|
|
|
|
|
|
debuglevel 1 |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# |
342
|
|
|
|
|
|
|
# ---- Backup Directives ---- |
343
|
|
|
|
|
|
|
# |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
hostname localhost |
347
|
|
|
|
|
|
|
backupschedule Mon, Wed, Fri |
348
|
|
|
|
|
|
|
archiveschedule Sun |
349
|
|
|
|
|
|
|
archivemaxdays 60 |
350
|
|
|
|
|
|
|
add / |
351
|
|
|
|
|
|
|
excl /home, /proc, /sys, /dev, /mnt, /media |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
hostname localhost |
356
|
|
|
|
|
|
|
backupschedule Mon, Wed, Fri |
357
|
|
|
|
|
|
|
archiveschedule Sun |
358
|
|
|
|
|
|
|
archivemaxdays 30 |
359
|
|
|
|
|
|
|
add /home |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
--- snap --- |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item * Setup procedure in perl context |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
#!/usr/bin/perl |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
use Config::MyConfig2; |
369
|
|
|
|
|
|
|
use strict; |
370
|
|
|
|
|
|
|
use Data::Dumper; |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
my $myconfig = Config::MyConfig2->new( |
373
|
|
|
|
|
|
|
conffile => "backup.cfg" |
374
|
|
|
|
|
|
|
); |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
my $conftemplate; |
377
|
|
|
|
|
|
|
$conftemplate->{global}->{rsync} = { required => 'true', type => 'single', match => '.+' }; |
378
|
|
|
|
|
|
|
$conftemplate->{global}->{sendmail} = { required => 'true', type => 'single', match => '.+' }; |
379
|
|
|
|
|
|
|
$conftemplate->{global}->{tar} = { required => 'true', type => 'single', match => '.+' }; |
380
|
|
|
|
|
|
|
$conftemplate->{global}->{ssh} = { required => 'true', type => 'single', match => '.+' }; |
381
|
|
|
|
|
|
|
$conftemplate->{global}->{rsync} = { required => 'true', type => 'single', match => '.+' }; |
382
|
|
|
|
|
|
|
$conftemplate->{global}->{debuglevel} = { required => 'true', type => 'single', match => '^\d$' }; |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
$conftemplate->{backup}->{hostname} = { required => 'true', type => 'single', match => '^[a-zA-Z0-9\.]+$' }; |
385
|
|
|
|
|
|
|
$conftemplate->{backup}->{backupschedule} = { required => 'true', type => 'list', match => '^[Mon]|[Tue]|[Wed]|[Thu]|[Fri]|[Sat]|[Sun]$' }; |
386
|
|
|
|
|
|
|
$conftemplate->{backup}->{archiveschedule} = { required => 'true', type => 'list', match => '^[Mon]|[Tue]|[Wed]|[Thu]|[Fri]|[Sat]|[Sun]$' }; |
387
|
|
|
|
|
|
|
$conftemplate->{backup}->{archivemaxdays} = { required => 'true', type => 'list', match => '^\d+$' }; |
388
|
|
|
|
|
|
|
$conftemplate->{backup}->{add} = { required => 'true', type => 'list', match => '.+' }; |
389
|
|
|
|
|
|
|
$conftemplate->{backup}->{excl} = { required => 'false', type => 'list', match => '.+' }; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
$myconfig->SetupDirectives($conftemplate); |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my $config = $myconfig->ReadConfig(); |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
print Dumper (\$config); |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=item * Results in the following hash structure |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
$VAR1 = \{ |
400
|
|
|
|
|
|
|
'global' => { |
401
|
|
|
|
|
|
|
'tar' => '/bin/tar', |
402
|
|
|
|
|
|
|
'sendmail' => '/usr/sbin/sendmail', |
403
|
|
|
|
|
|
|
'rsync' => '/usr/bin/rsync', |
404
|
|
|
|
|
|
|
'ssh' => '/usr/bin/ssh', |
405
|
|
|
|
|
|
|
'debuglevel' => '1' |
406
|
|
|
|
|
|
|
}, |
407
|
|
|
|
|
|
|
'backup' => { |
408
|
|
|
|
|
|
|
'server-home' => { |
409
|
|
|
|
|
|
|
'archivemaxdays' => [ |
410
|
|
|
|
|
|
|
'30' |
411
|
|
|
|
|
|
|
], |
412
|
|
|
|
|
|
|
'add' => [ |
413
|
|
|
|
|
|
|
'/home' |
414
|
|
|
|
|
|
|
], |
415
|
|
|
|
|
|
|
'archiveschedule' => [ |
416
|
|
|
|
|
|
|
'Sun' |
417
|
|
|
|
|
|
|
], |
418
|
|
|
|
|
|
|
'hostname' => 'localhost', |
419
|
|
|
|
|
|
|
'backupschedule' => [ |
420
|
|
|
|
|
|
|
'Mon', |
421
|
|
|
|
|
|
|
'Wed', |
422
|
|
|
|
|
|
|
'Fri' |
423
|
|
|
|
|
|
|
] |
424
|
|
|
|
|
|
|
}, |
425
|
|
|
|
|
|
|
'server-system' => { |
426
|
|
|
|
|
|
|
'excl' => [ |
427
|
|
|
|
|
|
|
'/home', |
428
|
|
|
|
|
|
|
'/proc', |
429
|
|
|
|
|
|
|
'/sys', |
430
|
|
|
|
|
|
|
'/dev', |
431
|
|
|
|
|
|
|
'/mnt', |
432
|
|
|
|
|
|
|
'/media' |
433
|
|
|
|
|
|
|
], |
434
|
|
|
|
|
|
|
'archivemaxdays' => [ |
435
|
|
|
|
|
|
|
'60' |
436
|
|
|
|
|
|
|
], |
437
|
|
|
|
|
|
|
'add' => [ |
438
|
|
|
|
|
|
|
'/' |
439
|
|
|
|
|
|
|
], |
440
|
|
|
|
|
|
|
'archiveschedule' => [ |
441
|
|
|
|
|
|
|
'Sun' |
442
|
|
|
|
|
|
|
], |
443
|
|
|
|
|
|
|
'hostname' => 'localhost', |
444
|
|
|
|
|
|
|
'backupschedule' => [ |
445
|
|
|
|
|
|
|
'Mon', |
446
|
|
|
|
|
|
|
'Wed', |
447
|
|
|
|
|
|
|
'Fri' |
448
|
|
|
|
|
|
|
] |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
}; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=back |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
A more advanced example can be found in the included example program myconfig-demo.pl. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=head1 NOTES |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Config::MyConfig2.pm supports my DebugHelper.pm class, which provides excellent |
460
|
|
|
|
|
|
|
debugging and error handling methods. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
$mycfg = Config::MyConfig2->new( |
463
|
|
|
|
|
|
|
conffile = "foo.cfg", |
464
|
|
|
|
|
|
|
dh = $reference_to_debughelper_class |
465
|
|
|
|
|
|
|
); |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
If you don't like, that MyConfig croaks if an error (i.e. syntax error in a configuration file) occurs, |
468
|
|
|
|
|
|
|
you may use MyConfig with eval: |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
eval { $myconfig->ReadConfig() } |
471
|
|
|
|
|
|
|
if ($@) ... do something |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=head1 AUTHOR |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Markus Guertler, C<< >> |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=head1 BUGS |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
480
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
481
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=head1 SUPPORT |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
perldoc Config::MyConfig2 |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
You can also look for information at: |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=over 4 |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
L |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
L |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=item * CPAN Ratings |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
L |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=item * Search CPAN |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
L |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=back |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
Copyright 2013 Markus Guertler. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
524
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
525
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=cut |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
package Config::MyConfig2; |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
our $VERSION = 2.19; |
536
|
|
|
|
|
|
|
|
537
|
2
|
|
|
2
|
|
177329
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
81
|
|
538
|
2
|
|
|
2
|
|
11
|
use Carp; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
11530
|
|
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
# Create object |
541
|
|
|
|
|
|
|
sub new |
542
|
|
|
|
|
|
|
{ |
543
|
1
|
|
|
1
|
1
|
954
|
my($class,%opts) = @_; |
544
|
1
|
|
|
|
|
3
|
my($self) = {}; |
545
|
1
|
|
|
|
|
3
|
bless ($self,$class); |
546
|
1
|
|
|
|
|
8
|
$self->{opts} = \%opts; |
547
|
1
|
|
|
|
|
4
|
return $self; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
# Setup directives |
551
|
|
|
|
|
|
|
sub SetupDirectives |
552
|
|
|
|
|
|
|
{ |
553
|
1
|
|
|
1
|
1
|
817
|
my $self = shift; |
554
|
1
|
|
|
|
|
3
|
my $directives = shift; |
555
|
1
|
50
|
|
|
|
6
|
$self->error ("No directives specified!") if (!$directives); |
556
|
1
|
|
|
|
|
4
|
$self->{Directives} = $directives; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
# Parse the configuration file and convert it into a hash tree |
560
|
|
|
|
|
|
|
sub ReadConfig |
561
|
|
|
|
|
|
|
{ |
562
|
1
|
|
|
1
|
1
|
472
|
my $self = shift; |
563
|
|
|
|
|
|
|
|
564
|
1
|
50
|
|
|
|
5
|
$self->error("Configuration File not specified!") if (! $self->{opts}->{conffile}); |
565
|
1
|
50
|
|
|
|
31
|
$self->error("Couldn't read configuration file $self->{opts}->{conffile}!") if (! -r $self->{opts}->{conffile}); |
566
|
|
|
|
|
|
|
|
567
|
1
|
|
|
|
|
2
|
my ($line,$directive_name,$directive_value); |
568
|
1
|
50
|
|
|
|
44
|
open CONF,"< ".$self->{opts}->{conffile} or $self->error("Could not open configuration file $self->{opts}->{conffile}"); |
569
|
1
|
|
|
|
|
22
|
while () |
570
|
|
|
|
|
|
|
{ |
571
|
1
|
|
|
|
|
3
|
chomp $_; |
572
|
1
|
|
|
|
|
3
|
$line++; |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# ignore comments |
575
|
1
|
50
|
33
|
|
|
21
|
if ($_ =~ /^\s*#.*$/ || $_ =~ /^\s*$/) |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
576
|
|
|
|
|
|
|
{ |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# open directive with identifier (multiple directives with different identifiers) |
580
|
|
|
|
|
|
|
elsif ($_ =~ /^\s*<([a-zA-Z0-9]+)\s+(.+)>\s*$/) |
581
|
|
|
|
|
|
|
{ |
582
|
0
|
0
|
|
|
|
0
|
$self->error ("Can't open directive <$1 $2>. Other directive already open!") if $directive_name; |
583
|
|
|
|
|
|
|
|
584
|
0
|
|
|
|
|
0
|
foreach (keys %{$self->{Directives}}) |
|
0
|
|
|
|
|
0
|
|
585
|
|
|
|
|
|
|
{ |
586
|
0
|
0
|
|
|
|
0
|
if ($_ eq $1) |
587
|
|
|
|
|
|
|
{ |
588
|
0
|
|
|
|
|
0
|
$directive_name = $1; |
589
|
0
|
|
|
|
|
0
|
$directive_value = $2; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
} |
592
|
0
|
0
|
|
|
|
0
|
$self->error ("Unknown directive: <$1 $2>") if !$directive_name; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# close directive |
596
|
|
|
|
|
|
|
elsif ($_ =~ /^\s*<\/([a-zA-Z0-9]+)>\s*$/) |
597
|
|
|
|
|
|
|
{ |
598
|
0
|
0
|
|
|
|
0
|
if ($1 ne $directive_name) |
599
|
|
|
|
|
|
|
{ |
600
|
0
|
|
|
|
|
0
|
$self->error ("Close of a not openend directive: $1> !"); |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
else |
603
|
|
|
|
|
|
|
{ |
604
|
0
|
|
|
|
|
0
|
undef $directive_name; |
605
|
0
|
|
|
|
|
0
|
undef $directive_value; |
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# keyword identification |
610
|
|
|
|
|
|
|
elsif ($_ =~ /^\s*(.+?)[\s\t\=]+(.*)\s*$/) |
611
|
|
|
|
|
|
|
{ |
612
|
1
|
50
|
|
|
|
3
|
if ($directive_name) |
613
|
|
|
|
|
|
|
{ |
614
|
0
|
|
|
|
|
0
|
$self->_ConfigDirective ($1,$2,$line,$directive_name,$directive_value) |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
else |
617
|
|
|
|
|
|
|
{ |
618
|
1
|
|
|
|
|
7
|
$self->_ConfigDirective ($1,$2,$line,'global') |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
else |
622
|
|
|
|
|
|
|
{ |
623
|
0
|
|
|
|
|
0
|
$self->error("Syntax error in configfile line $line"); |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
1
|
|
|
|
|
11
|
close CONF; |
628
|
1
|
|
|
|
|
5
|
$self->_CheckRequired; |
629
|
1
|
|
|
|
|
4
|
return $self->{config}; |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# Parse and write the values of the keywords in the proper section (directive) of the config hash-tree |
633
|
|
|
|
|
|
|
# If line is -1, it indicates, that _ConfigDirective is called from a SetValue method: In this case, the method returns with the error message instead of throwing an error |
634
|
|
|
|
|
|
|
sub _ConfigDirective |
635
|
|
|
|
|
|
|
{ |
636
|
1
|
|
|
1
|
|
1
|
my $self = shift; |
637
|
1
|
|
|
|
|
4
|
my ($keyword,$value,$line,$directive_name,$directive_value) = @_; |
638
|
1
|
|
|
|
|
2
|
my ($key); |
639
|
|
|
|
|
|
|
my $foundflag; |
640
|
0
|
|
|
|
|
0
|
my @multival; |
641
|
|
|
|
|
|
|
|
642
|
1
|
|
|
|
|
3
|
foreach $key (keys %{$self->{Directives}->{$directive_name}}) |
|
1
|
|
|
|
|
4
|
|
643
|
|
|
|
|
|
|
{ |
644
|
|
|
|
|
|
|
# Keyword defined directive? |
645
|
1
|
50
|
|
|
|
31
|
if ($keyword eq $key) |
646
|
|
|
|
|
|
|
{ |
647
|
|
|
|
|
|
|
# Keyword a list of keywords? |
648
|
1
|
50
|
|
|
|
6
|
if ($self->{Directives}->{$directive_name}->{$key}->{type} eq 'list') |
649
|
|
|
|
|
|
|
{ |
650
|
0
|
|
|
|
|
0
|
@multival = split(/,\s*/,$value); |
651
|
0
|
|
|
|
|
0
|
foreach (@multival) |
652
|
|
|
|
|
|
|
{ |
653
|
|
|
|
|
|
|
# Do all values match the configured conditions (match)? |
654
|
0
|
0
|
|
|
|
0
|
if ($_ !~ $self->{Directives}->{$directive_name}->{$key}->{match}) |
655
|
|
|
|
|
|
|
{ |
656
|
0
|
|
|
|
|
0
|
$line = -1 ? return "Syntax error (value): $keyword -> $value" : $self->error("Syntax error (value) in configfile line $line: $keyword near $_"); |
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
# Keyword a single keyword? |
660
|
|
|
|
|
|
|
} else |
661
|
|
|
|
|
|
|
{ |
662
|
|
|
|
|
|
|
# Does the value matches the configured condition (match)? |
663
|
1
|
50
|
|
|
|
25
|
if ($value !~$self->{Directives}->{$directive_name}->{$key}->{match}) |
664
|
|
|
|
|
|
|
{ |
665
|
0
|
|
|
|
|
0
|
$line = -1 ? return "Syntax error (value): $keyword -> $value" : $self->error("Syntax error (value) in configfile line $line: $keyword $value"); |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# Global directive or directive without identifier? |
670
|
1
|
50
|
33
|
|
|
7
|
if ($directive_name eq 'global' or !$directive_value) |
671
|
|
|
|
|
|
|
{ |
672
|
|
|
|
|
|
|
# If the keyword is of type list, then all values are pushed in an array |
673
|
1
|
50
|
|
|
|
6
|
if ($self->{Directives}->{$directive_name}->{$key}->{type} eq 'list') |
674
|
|
|
|
|
|
|
{ |
675
|
0
|
|
|
|
|
0
|
push (@{$self->{config}->{$directive_name}->{$key}},@multival); |
|
0
|
|
|
|
|
0
|
|
676
|
|
|
|
|
|
|
# otherwise, store a single value without creating an array |
677
|
|
|
|
|
|
|
} else |
678
|
|
|
|
|
|
|
{ |
679
|
1
|
|
|
|
|
4
|
$self->{config}->{$directive_name}->{$key}=$value; |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
# Dedicated directive? |
682
|
|
|
|
|
|
|
} else |
683
|
|
|
|
|
|
|
{ |
684
|
|
|
|
|
|
|
# If the keyword is of type list, then all values are pushed in an array |
685
|
0
|
0
|
|
|
|
0
|
if ($self->{Directives}->{$directive_name}->{$key}->{type} eq 'list') |
686
|
|
|
|
|
|
|
{ |
687
|
0
|
|
|
|
|
0
|
push (@{$self->{config}->{$directive_name}->{$directive_value}->{$key}},@multival); |
|
0
|
|
|
|
|
0
|
|
688
|
|
|
|
|
|
|
# otherwise, store a single value without creating an array |
689
|
|
|
|
|
|
|
} else |
690
|
|
|
|
|
|
|
{ |
691
|
0
|
|
|
|
|
0
|
$self->{config}->{$directive_name}->{$directive_value}->{$key}=$value; |
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
# Indicate, that the keyword has been found in the list of all configured keywords |
695
|
1
|
|
|
|
|
4
|
$foundflag = 1; |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
# If the keyword hasn't been found in the list of all configured keywords, it's an error in the configuration file |
699
|
1
|
50
|
|
|
|
12
|
if (!$foundflag) |
700
|
|
|
|
|
|
|
{ |
701
|
0
|
|
|
|
|
0
|
$line = -1 ? return "Syntax error (keyword): $keyword -> $value" : $self->error("Syntax error (keyword) in configfile line $line: $keyword $value") |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
} |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
sub _CheckRequired |
707
|
|
|
|
|
|
|
{ |
708
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
709
|
1
|
|
|
|
|
2
|
my $found; |
710
|
|
|
|
|
|
|
# For each directive in the config template |
711
|
1
|
|
|
|
|
2
|
foreach my $directive (keys %{$self->{Directives}}) |
|
1
|
|
|
|
|
4
|
|
712
|
|
|
|
|
|
|
{ |
713
|
|
|
|
|
|
|
# and for each keyword in a directive |
714
|
1
|
|
|
|
|
2
|
foreach my $keyword (keys %{$self->{Directives}->{$directive}}) |
|
1
|
|
|
|
|
3
|
|
715
|
|
|
|
|
|
|
{ |
716
|
|
|
|
|
|
|
# check if the required option is set for this keyword of this directive in the config template |
717
|
|
|
|
|
|
|
# AND if this keyword is NOT already defined config hashtree (what would mean that it is in the configuration file and |
718
|
|
|
|
|
|
|
# the requirement is fullfilled) |
719
|
1
|
50
|
33
|
|
|
13
|
if ($self->{Directives}->{$directive}->{$keyword}->{required} eq 'true' and !defined $self->{config}->{$directive}->{$keyword}) |
720
|
|
|
|
|
|
|
{ |
721
|
|
|
|
|
|
|
# For the global directive, it is not required to cycle through to subdirectives |
722
|
0
|
0
|
|
|
|
0
|
if ($directive eq 'global') |
723
|
|
|
|
|
|
|
{ |
724
|
0
|
|
|
|
|
0
|
$self->error("Required keyword $keyword not found in configfile directive $directive") |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
# Go through all directives (that might be either keywords or subdirectives) |
727
|
0
|
|
|
|
|
0
|
foreach my $subdirective (keys %{$self->{config}->{$directive}}) |
|
0
|
|
|
|
|
0
|
|
728
|
|
|
|
|
|
|
{ |
729
|
|
|
|
|
|
|
# If it is a subdirective it must be hash |
730
|
0
|
0
|
|
|
|
0
|
if (ref($self->{config}->{$directive}->{$subdirective}) eq "HASH") |
731
|
|
|
|
|
|
|
{ |
732
|
|
|
|
|
|
|
# if the current keyword is not defined in the subdirective, the requirement is not fullfilled |
733
|
0
|
0
|
|
|
|
0
|
if (!defined $self->{config}->{$directive}->{$subdirective}->{$keyword}) |
734
|
|
|
|
|
|
|
{ |
735
|
0
|
|
|
|
|
0
|
$self->error("Required keyword $keyword not found in configfile directive $directive, subdirective $subdirective") |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
# If it is not a hash, it is no subdirective, so it must be a keyword |
738
|
|
|
|
|
|
|
# Since the keyword is not defined, but required (see first if clause), an error is thrown |
739
|
|
|
|
|
|
|
} else |
740
|
|
|
|
|
|
|
{ |
741
|
0
|
|
|
|
|
0
|
$self->error("Required keyword $keyword not found in configfile directive $directive") |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
} |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# Writes the configuration to a file or to the original file, if file is omitted |
751
|
|
|
|
|
|
|
sub WriteConfig |
752
|
|
|
|
|
|
|
{ |
753
|
1
|
|
|
1
|
1
|
400
|
my $self = shift; |
754
|
1
|
|
|
|
|
2
|
my $name = shift; |
755
|
1
|
|
|
|
|
2
|
my $filename = shift; |
756
|
1
|
50
|
|
|
|
5
|
$self->error("Please specify a valid filename for writing the new configuration!") if (!$filename); |
757
|
|
|
|
|
|
|
|
758
|
1
|
50
|
|
|
|
105
|
open CONF,"> ".$filename or $self->error("Could not open configuration file $filename for writing!"); |
759
|
|
|
|
|
|
|
|
760
|
1
|
|
|
|
|
13
|
print CONF "#\n#\n"; |
761
|
1
|
|
|
|
|
5
|
print CONF "# $name\n"; |
762
|
1
|
|
|
|
|
2
|
print CONF "#\n#\n"; |
763
|
|
|
|
|
|
|
|
764
|
1
|
|
|
|
|
2
|
print CONF "\n# Global vlaues\n"; |
765
|
|
|
|
|
|
|
|
766
|
1
|
|
|
|
|
2
|
my $val; |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# First write the global values |
769
|
1
|
|
|
|
|
3
|
my $base = $self->{config}->{global}; |
770
|
1
|
|
|
|
|
5
|
$self->_WriteKeysValues($base, *CONF); |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# Secondly write all directives |
773
|
1
|
|
|
|
|
4
|
my @directives = $self->GetDirectiveNames(); |
774
|
|
|
|
|
|
|
|
775
|
1
|
50
|
|
|
|
4
|
if (@directives) |
776
|
|
|
|
|
|
|
{ |
777
|
0
|
|
|
|
|
0
|
print CONF "\n# Directives\n"; |
778
|
|
|
|
|
|
|
|
779
|
0
|
|
|
|
|
0
|
foreach my $directive (@directives) |
780
|
|
|
|
|
|
|
{ |
781
|
0
|
|
|
|
|
0
|
my @identifiers = $self->GetDirectiveIdentifiers($directive); |
782
|
0
|
|
|
|
|
0
|
foreach my $identifier (@identifiers) |
783
|
|
|
|
|
|
|
{ |
784
|
0
|
|
|
|
|
0
|
print CONF "<$directive $identifier>\n"; |
785
|
0
|
|
|
|
|
0
|
$base = $self->{config}->{$directive}->{$identifier}; |
786
|
0
|
|
|
|
|
0
|
$self->_WriteKeysValues($base,*CONF,"\t"); |
787
|
0
|
|
|
|
|
0
|
print CONF "$directive>\n\n"; |
788
|
|
|
|
|
|
|
} |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
} |
791
|
1
|
|
|
|
|
36
|
close CONF; |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# Write keys and values, called by WriteConfig |
795
|
|
|
|
|
|
|
sub _WriteKeysValues |
796
|
|
|
|
|
|
|
{ |
797
|
1
|
|
|
1
|
|
1
|
my $self = shift; |
798
|
1
|
|
|
|
|
3
|
my $base = shift; |
799
|
1
|
|
|
|
|
3
|
my $handle = shift; |
800
|
1
|
|
50
|
|
|
6
|
my $trail = shift || ''; |
801
|
|
|
|
|
|
|
|
802
|
1
|
|
|
|
|
1
|
my $val; |
803
|
|
|
|
|
|
|
|
804
|
1
|
|
|
|
|
2
|
foreach my $key (sort keys (%{$base})) |
|
1
|
|
|
|
|
5
|
|
805
|
|
|
|
|
|
|
{ |
806
|
1
|
50
|
|
|
|
4
|
if (ref($base->{$key}) eq 'ARRAY') |
807
|
|
|
|
|
|
|
{ |
808
|
0
|
|
|
|
|
0
|
foreach (@{$base->{$key}}) |
|
0
|
|
|
|
|
0
|
|
809
|
|
|
|
|
|
|
{ |
810
|
0
|
|
|
|
|
0
|
$val = $_; |
811
|
0
|
|
|
|
|
0
|
print $handle "$trail$key $val\n"; |
812
|
|
|
|
|
|
|
} |
813
|
|
|
|
|
|
|
} else |
814
|
|
|
|
|
|
|
{ |
815
|
1
|
|
|
|
|
2
|
$val = $base->{$key}; |
816
|
1
|
|
|
|
|
14
|
print $handle "$trail$key $val\n"; |
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
# Returns a reference to the configuration |
823
|
|
|
|
|
|
|
sub GetConfigRef |
824
|
|
|
|
|
|
|
{ |
825
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
826
|
0
|
|
|
|
|
0
|
return $self->{config}; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# Returns a global value or undef |
830
|
|
|
|
|
|
|
sub GetGlobalValue |
831
|
|
|
|
|
|
|
{ |
832
|
1
|
|
|
1
|
1
|
419
|
my $self = shift; |
833
|
1
|
|
|
|
|
2
|
my $key = shift; |
834
|
|
|
|
|
|
|
|
835
|
1
|
|
|
|
|
5
|
return($self->{config}->{global}->{$key}); |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
# Returns a value from a directive or undef |
839
|
|
|
|
|
|
|
sub GetDirectiveValue |
840
|
|
|
|
|
|
|
{ |
841
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
842
|
0
|
|
|
|
|
0
|
my $directive = shift; |
843
|
0
|
|
|
|
|
0
|
my $identifier = shift; |
844
|
0
|
|
|
|
|
0
|
my $key = shift; |
845
|
|
|
|
|
|
|
|
846
|
0
|
|
|
|
|
0
|
return($self->{config}->{$directive}->{$identifier}->{$key}); |
847
|
|
|
|
|
|
|
} |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
# Deletes an identifier and value from a directive and returns the removed |
850
|
|
|
|
|
|
|
# values or undef if the directive/identifier combination doesn't exist |
851
|
|
|
|
|
|
|
sub DeleteDirectiveIdentifier |
852
|
|
|
|
|
|
|
{ |
853
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
854
|
0
|
|
|
|
|
0
|
my $directive = shift; |
855
|
0
|
|
|
|
|
0
|
my $identifier = shift; |
856
|
0
|
|
|
|
|
0
|
return(delete($self->{config}->{$directive}->{$identifier})); |
857
|
|
|
|
|
|
|
} |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
# Returns all directive names (except global) as a list or undef |
860
|
|
|
|
|
|
|
sub GetDirectiveNames |
861
|
|
|
|
|
|
|
{ |
862
|
1
|
|
|
1
|
1
|
1
|
my $self = shift; |
863
|
1
|
|
|
|
|
2
|
my @directives; |
864
|
1
|
|
|
|
|
2
|
foreach (sort keys %{$self->{config}}) |
|
1
|
|
|
|
|
4
|
|
865
|
|
|
|
|
|
|
{ |
866
|
1
|
50
|
|
|
|
5
|
next if ($_ eq 'global'); |
867
|
0
|
|
|
|
|
0
|
push (@directives,$_); |
868
|
|
|
|
|
|
|
} |
869
|
1
|
|
|
|
|
3
|
return(@directives); |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
# Returns all directive identifiers (except global) as a list or undef |
873
|
|
|
|
|
|
|
sub GetDirectiveIdentifiers |
874
|
|
|
|
|
|
|
{ |
875
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
876
|
0
|
|
|
|
|
|
my $name = shift; |
877
|
0
|
0
|
|
|
|
|
$self->error("No directive specified for GetDirectiveIdentifiers!") if (!$name); |
878
|
0
|
0
|
|
|
|
|
$self->error("Directive name can't be 'global' for GetDirectiveIdentifiers") if ($name eq 'global'); |
879
|
0
|
|
|
|
|
|
my @identifiers; |
880
|
0
|
|
|
|
|
|
foreach (sort keys %{$self->{config}->{$name}}) |
|
0
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
{ |
882
|
0
|
|
|
|
|
|
push (@identifiers,$_); |
883
|
|
|
|
|
|
|
} |
884
|
0
|
|
|
|
|
|
return(@identifiers); |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
# Sets a global directive value |
888
|
|
|
|
|
|
|
sub SetGlobalValue |
889
|
|
|
|
|
|
|
{ |
890
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
891
|
0
|
|
|
|
|
|
my $key = shift; |
892
|
0
|
|
|
|
|
|
my $val = shift; |
893
|
|
|
|
|
|
|
|
894
|
0
|
|
|
|
|
|
my $base = $self->{config}->{global}; |
895
|
0
|
|
|
|
|
|
my $error = $self->_ConfigDirective($key,$val,'-1','global'); |
896
|
0
|
|
|
|
|
|
return ($error); |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
# Sets a value within a directive |
900
|
|
|
|
|
|
|
sub SetDirectiveValue |
901
|
|
|
|
|
|
|
{ |
902
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
903
|
0
|
|
|
|
|
|
my $directive = shift; |
904
|
0
|
|
|
|
|
|
my $identifier = shift; |
905
|
0
|
|
|
|
|
|
my $key = shift; |
906
|
0
|
|
|
|
|
|
my $val = shift; |
907
|
|
|
|
|
|
|
|
908
|
0
|
|
|
|
|
|
my $base = $self->{config}->{global}; |
909
|
0
|
|
|
|
|
|
my $error = $self->_ConfigDirective($key,$val,'-1',$directive,$identifier); |
910
|
0
|
|
|
|
|
|
return ($error); |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# Error handling |
914
|
|
|
|
|
|
|
sub error |
915
|
|
|
|
|
|
|
{ |
916
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
917
|
0
|
|
|
|
|
|
my $errmsg = shift; |
918
|
|
|
|
|
|
|
|
919
|
0
|
0
|
|
|
|
|
if (exists $self->{opts}->{dh}) |
920
|
|
|
|
|
|
|
{ |
921
|
0
|
|
|
|
|
|
$self->{opts}->{dh}->error("$errmsg"); |
922
|
|
|
|
|
|
|
} else |
923
|
|
|
|
|
|
|
{ |
924
|
0
|
|
|
|
|
|
croak "Error: $errmsg\n"; |
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
} |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
1; |