| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Apache::Config::Preproc; | 
| 2 | 24 |  |  | 24 |  | 185309 | use parent 'Apache::Admin::Config'; | 
|  | 24 |  |  |  |  | 205 |  | 
|  | 24 |  |  |  |  | 157 |  | 
| 3 | 24 |  |  | 24 |  | 520058 | use strict; | 
|  | 24 |  |  |  |  | 61 |  | 
|  | 24 |  |  |  |  | 577 |  | 
| 4 | 24 |  |  | 24 |  | 119 | use warnings; | 
|  | 24 |  |  |  |  | 46 |  | 
|  | 24 |  |  |  |  | 661 |  | 
| 5 | 24 |  |  | 24 |  | 134 | use Carp; | 
|  | 24 |  |  |  |  | 45 |  | 
|  | 24 |  |  |  |  | 1553 |  | 
| 6 | 24 |  |  | 24 |  | 11944 | use version 0.77; | 
|  | 24 |  |  |  |  | 53287 |  | 
|  | 24 |  |  |  |  | 2000 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | our $VERSION = '1.07'; | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | sub import { | 
| 11 | 33 |  |  | 33 |  | 912906 | my $class = shift; | 
| 12 | 33 | 50 |  |  |  | 205 | if (defined(my $kw = shift)) { | 
| 13 | 0 | 0 |  |  |  | 0 | if ($kw eq ':default') { | 
|  |  | 0 |  |  |  |  |  | 
| 14 | 0 |  |  |  |  | 0 | install_preproc_default() | 
| 15 |  |  |  |  |  |  | } elsif ($kw eq ':optimized') { | 
| 16 | 0 |  |  |  |  | 0 | install_preproc_optimized() | 
| 17 |  |  |  |  |  |  | } else { | 
| 18 | 0 |  |  |  |  | 0 | croak "Unrecognized import parameter: $kw" | 
| 19 |  |  |  |  |  |  | } | 
| 20 |  |  |  |  |  |  | } | 
| 21 | 33 | 50 |  |  |  | 160 | if (@_) { | 
| 22 | 0 |  |  |  |  | 0 | croak "Too many import parameters"; | 
| 23 |  |  |  |  |  |  | } | 
| 24 | 33 |  |  |  |  | 35835 | $class->SUPER::import(); | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | sub new { | 
| 28 | 31 |  |  | 31 | 1 | 185760 | my $class = shift; | 
| 29 | 31 |  |  |  |  | 81 | my $file = shift; | 
| 30 | 31 |  | 100 |  |  | 198 | my $explist = Apache::Admin::Config::Tree::_get_arg(\@_, '-expand') | 
| 31 |  |  |  |  |  |  | || [ qw(include) ]; | 
| 32 |  |  |  |  |  |  |  | 
| 33 | 31 | 50 |  |  |  | 1255 | my $self = $class->SUPER::new($file, @_) or return; | 
| 34 | 31 |  |  |  |  | 25282 | bless $self, $class; | 
| 35 | 31 |  |  |  |  | 116 | $self->{_filename} = $file; | 
| 36 | 31 |  |  |  |  | 102 | $self->{_options} = \@_; | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 31 |  |  |  |  | 67 | eval { | 
| 39 | 31 |  |  |  |  | 239 | $self->_preproc($explist); | 
| 40 |  |  |  |  |  |  | }; | 
| 41 | 31 | 100 |  |  |  | 497 | if ($@) { | 
| 42 | 1 |  |  |  |  | 2 | $Apache::Admin::Config::ERROR = $@; | 
| 43 | 1 |  |  |  |  | 10 | return; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 30 |  |  |  |  | 143 | return $self; | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 9 |  |  | 9 | 1 | 32 | sub filename { shift->{_filename} } | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | sub dequote { | 
| 52 | 28 |  |  | 28 | 0 | 156 | my ($self, $str) = @_; | 
| 53 | 28 | 100 |  |  |  | 195 | if ($str =~ s/^"(.*)"$/$1/) { | 
| 54 | 10 |  |  |  |  | 30 | $str =~ s/\\"/"/g; | 
| 55 |  |  |  |  |  |  | } | 
| 56 | 28 |  |  |  |  | 80 | return $str; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 | 22 |  |  | 22 | 1 | 28 | sub options { @{shift->{_options}} } | 
|  | 22 |  |  |  |  | 119 |  | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub _preproc { | 
| 62 | 31 |  |  | 31 |  | 100 | my ($self, $explist) = @_; | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | $self->_preproc_section($self, | 
| 65 |  |  |  |  |  |  | [ map { | 
| 66 | 31 |  |  |  |  | 109 | my ($mod,@arg); | 
|  | 41 |  |  |  |  | 337 |  | 
| 67 | 41 | 100 |  |  |  | 198 | if (ref($_) eq 'HASH') { | 
|  |  | 50 |  |  |  |  |  | 
| 68 | 3 |  |  |  |  | 28 | ($mod,my $ref) = each %$_; | 
| 69 | 3 |  |  |  |  | 11 | @arg = @$ref; | 
| 70 |  |  |  |  |  |  | } elsif (ref($_) eq 'ARRAY') { | 
| 71 | 0 |  |  |  |  | 0 | @arg = @$_; | 
| 72 | 0 |  |  |  |  | 0 | $mod = shift @arg; | 
| 73 |  |  |  |  |  |  | } else { | 
| 74 | 38 |  |  |  |  | 170 | $mod = $_; | 
| 75 |  |  |  |  |  |  | } | 
| 76 | 41 |  |  |  |  | 119 | $mod = 'Apache::Config::Preproc::'.$mod; | 
| 77 | 41 |  |  |  |  | 250 | (my $file = $mod) =~ s|::|/|g; | 
| 78 | 41 |  |  |  |  | 14622 | require $file . '.pm'; | 
| 79 | 41 |  |  |  |  | 352 | $mod->new($self, @arg) | 
| 80 |  |  |  |  |  |  | } @$explist ]); | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | # As of version 0.95, the Apache::Admin::Config package provides no | 
| 84 |  |  |  |  |  |  | # methods for iterating over all configuration file statements, excepting | 
| 85 |  |  |  |  |  |  | # the select method with the -which => N argument, which returns Nth | 
| 86 |  |  |  |  |  |  | # statement or undef if N is out of range.  This method has two drawbacks: | 
| 87 |  |  |  |  |  |  | # | 
| 88 |  |  |  |  |  |  | #   1. It iterates over entire statement tree no matter what arguments are | 
| 89 |  |  |  |  |  |  | #      given (see Apache/Admin/Config.pm, lines 417-439) | 
| 90 |  |  |  |  |  |  | #   2. It makes unnecessary memory allocations (ibid., line 437). | 
| 91 |  |  |  |  |  |  | #   3. When N is out of range, the following warning is emitted | 
| 92 |  |  |  |  |  |  | #      in -w mode: | 
| 93 |  |  |  |  |  |  | #         Use of uninitialized value $_[0] in string at | 
| 94 |  |  |  |  |  |  | #         /usr/share/perl5/overload.pm line 119 | 
| 95 |  |  |  |  |  |  | #      That's because it unreferences the undefined value and passes it | 
| 96 |  |  |  |  |  |  | #      to the overload::StrVal method (ibid., line 443). | 
| 97 |  |  |  |  |  |  | # | 
| 98 |  |  |  |  |  |  | # This means that time complexity of the code below is O(N**2).  This is | 
| 99 |  |  |  |  |  |  | # further aggravated by the fact that no method is provided for inline | 
| 100 |  |  |  |  |  |  | # modification of the source tree, except for the 'add' method, which again | 
| 101 |  |  |  |  |  |  | # iterates over entire tree in order to locate the element, after which | 
| 102 |  |  |  |  |  |  | # the new one should be inserted. | 
| 103 |  |  |  |  |  |  | # | 
| 104 |  |  |  |  |  |  | # Thus, the following default implementation of the _preproc_section function | 
| 105 |  |  |  |  |  |  | # is highly inefficient: | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub _preproc_section_default { | 
| 108 | 0 |  |  | 0 |  | 0 | my ($self, $section, $modlist) = @_; | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 0 | 0 |  |  |  | 0 | return unless @$modlist; | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 0 |  |  |  |  | 0 | $_->begin_section($section) foreach (@$modlist); | 
| 113 |  |  |  |  |  |  | OUTER: | 
| 114 | 0 |  |  |  |  | 0 | for (my $i = 0; | 
| 115 |  |  |  |  |  |  | defined(my $d = do { | 
| 116 |  |  |  |  |  |  | local $SIG{__WARN__} = sub { | 
| 117 | 0 |  |  | 0 |  | 0 | my $msg = shift; | 
| 118 | 0 | 0 |  |  |  | 0 | warn "$msg" unless $msg =~ /uninitialized/; | 
| 119 | 0 |  |  |  |  | 0 | }; | 
| 120 | 0 |  |  |  |  | 0 | $section->select(-which => $i) }); ) { | 
| 121 | 0 |  |  |  |  | 0 | foreach my $mod (@$modlist) { | 
| 122 | 0 | 0 |  |  |  | 0 | if ($mod->expand($d, \my @repl)) { | 
| 123 | 0 |  |  |  |  | 0 | my $prev = $d; | 
| 124 | 0 |  |  |  |  | 0 | foreach my $r (@repl) { | 
| 125 | 0 |  |  |  |  | 0 | $prev = $section->add($r, -after => $prev); | 
| 126 |  |  |  |  |  |  | } | 
| 127 | 0 |  |  |  |  | 0 | $d->unlink; | 
| 128 | 0 |  |  |  |  | 0 | next OUTER; | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 0 | 0 |  |  |  | 0 | if ($d->type eq 'section') { | 
| 131 | 0 |  |  |  |  | 0 | $self->_preproc_section_default($d, $modlist); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 | 0 |  |  |  |  | 0 | $i++; | 
| 135 |  |  |  |  |  |  | } | 
| 136 | 0 |  |  |  |  | 0 | $_->end_section($section) foreach (@$modlist); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # In an attempt to fix the above problems I resort to a kludgy solution, | 
| 140 |  |  |  |  |  |  | # which directly modifies the Apache::Admin::Config::Tree namespace | 
| 141 |  |  |  |  |  |  | # and defines two missing functions in it: get_nth(N), which returns | 
| 142 |  |  |  |  |  |  | # the Nth statement or undef if N is greater than the source tree | 
| 143 |  |  |  |  |  |  | # length, and replace_inplace(N, A), which replaces the Nth statement | 
| 144 |  |  |  |  |  |  | # with statements from the array A.  With these two methods at hand, | 
| 145 |  |  |  |  |  |  | # the following implementation is used: | 
| 146 |  |  |  |  |  |  | sub _preproc_section_optimized { | 
| 147 | 240 |  |  | 240 |  | 394 | my ($self, $section, $modlist) = @_; | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 240 | 50 |  |  |  | 430 | return unless @$modlist; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 240 |  |  |  |  | 624 | $_->begin_section($section) foreach (@$modlist); | 
| 152 |  |  |  |  |  |  | OUTER: | 
| 153 | 240 |  |  |  |  | 749 | for (my $i = 0; defined(my $d = $section->get_nth($i)); ) { | 
| 154 | 736 |  |  |  |  | 1048 | foreach my $mod (@$modlist) { | 
| 155 | 2037 | 100 |  |  |  | 6030 | if ($mod->expand($d, \my @repl)) { | 
| 156 | 229 |  |  |  |  | 1220 | $section->replace_inplace($i, @repl); | 
| 157 | 229 |  |  |  |  | 1465 | next OUTER; | 
| 158 |  |  |  |  |  |  | } | 
| 159 | 1807 | 100 |  |  |  | 5003 | if ($d->type eq 'section') { | 
| 160 | 209 |  |  |  |  | 857 | $self->_preproc_section_optimized($d, $modlist); | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } | 
| 163 | 506 |  |  |  |  | 2307 | $i++; | 
| 164 |  |  |  |  |  |  | } | 
| 165 | 239 |  |  |  |  | 647 | $_->end_section($section) foreach (@$modlist); | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | # The _preproc_section method upon its first invocation selects the | 
| 169 |  |  |  |  |  |  | # right implementation to use.  If the version of the Apache::Admin::Config | 
| 170 |  |  |  |  |  |  | # module is 0.95 or if the object has attribute {tree}{children} and it is | 
| 171 |  |  |  |  |  |  | # a list reference, the function installs the two new methods in the | 
| 172 |  |  |  |  |  |  | # Apache::Admin::Config::Tree namespace and selects the optimized | 
| 173 |  |  |  |  |  |  | # implementation.  Otherwise, the default implementation is used. | 
| 174 |  |  |  |  |  |  | # | 
| 175 |  |  |  |  |  |  | # The decision can be forced when requiring the module.  To select the | 
| 176 |  |  |  |  |  |  | # default implementation, do | 
| 177 |  |  |  |  |  |  | # | 
| 178 |  |  |  |  |  |  | #   use Apache::Config::Preproc qw(:default); | 
| 179 |  |  |  |  |  |  | # | 
| 180 |  |  |  |  |  |  | # To select the optimized implementation: | 
| 181 |  |  |  |  |  |  | # | 
| 182 |  |  |  |  |  |  | #   use Apache::Config::Preproc qw(:optimized); | 
| 183 |  |  |  |  |  |  | # | 
| 184 |  |  |  |  |  |  | sub _preproc_section { | 
| 185 | 31 |  |  | 31 |  | 76 | my $self = shift; | 
| 186 | 31 | 100 |  |  |  | 401 | unless ($self->can('_preproc_section_internal')) { | 
| 187 | 24 | 50 | 0 |  |  | 496 | if ((version->parse($Apache::Admin::Config::VERSION) == version->parse('0.95') | 
|  |  |  | 33 |  |  |  |  | 
| 188 |  |  |  |  |  |  | || (exists($self->{children}) && ref($self->{tree}{children}) eq 'ARRAY'))) { | 
| 189 | 24 |  |  |  |  | 93 | install_preproc_optimized() | 
| 190 |  |  |  |  |  |  | } else { | 
| 191 | 0 |  |  |  |  | 0 | install_preproc_default() | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  | } | 
| 194 | 31 |  |  |  |  | 536 | $self->_preproc_section_internal(@_); | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub install_preproc_optimized { | 
| 198 | 24 |  |  | 24 |  | 47158 | no warnings 'once'; | 
|  | 24 |  |  |  |  | 2096 |  | 
|  | 24 |  |  |  |  | 6338 |  | 
| 199 |  |  |  |  |  |  | *{Apache::Admin::Config::Tree::get_nth} = sub { | 
| 200 | 975 |  |  | 975 |  | 4314 | my ($self, $n) = @_; | 
| 201 | 975 | 100 |  |  |  | 1018 | if ($n < @{$self->{children}}) { | 
|  | 975 |  |  |  |  | 2505 |  | 
| 202 | 736 |  |  |  |  | 1789 | return $self->{children}[$n]; | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  | return undef | 
| 205 | 239 |  |  | 24 | 0 | 495 | }; | 
|  | 24 |  |  |  |  | 206 |  | 
| 206 |  |  |  |  |  |  | *{Apache::Admin::Config::Tree::replace_inplace} = sub { | 
| 207 | 229 |  |  | 229 |  | 1958 | my ($self, $n, @items) = @_; | 
| 208 | 229 |  |  |  |  | 593 | splice @{$self->{children}}, $n, 1, | 
| 209 | 229 |  |  |  |  | 354 | map { $_->{parent} = $self; $_ } @items; | 
|  | 234 |  |  |  |  | 351 |  | 
|  | 234 |  |  |  |  | 572 |  | 
| 210 | 24 |  |  |  |  | 127 | }; | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 24 |  |  |  |  | 129 | *{_preproc_section_internal} = \&_preproc_section_optimized; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | sub install_preproc_default { | 
| 216 | 0 |  |  | 0 | 0 |  | *{_preproc_section_internal} = \&_preproc_section_default; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | 1; | 
| 220 |  |  |  |  |  |  | __END__ |