LXR Cross Referencer Git repo
Brought to you by:
ajlittoz
#!/usr/bin/perl -T ###################################################################### # # showconfig -- Present LXR configuration as html # # Andre J Littoz <ajlittoz@users.sf.net> # # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ###################################################################### =head1 script showconfig This script shows how LXR understood the configuration parameters from F<lxr.conf> file. They are displayed in tabular form: First column: parameter name Second column: parameter type (I<string>, I<array>, I<hash>, ...) Third column: value from tree-specific parameter group Fourth column: value from global parameter group With such a layout, it is easy to see if a global value is overridden by a specific one. =cut use strict; use lib do { $0 =~ m{(.*)/} ? "$1/lib" : 'lib' }; # if LXR modules are in ./lib use LXR::Common; use LXR::Template; =head2 C<dumphash ($h, $left)> Function C<dumphash> returns the contents of the input hash as a ready-to-print string. The value of a key may be a simple I<string> (displayed surrounded with quotes), an I<array> (dumped "as is" without checking for further references) or a I<hash> (recursively dumped surrounded with curly braces). =over =item 1 C<$h> a reference to the I<hash> to dump =item 2 C<$left> the I<number> of spaces at left to indent this hash =back =cut sub dumphash { my $h = shift; my $left = shift; my $d = ' 'x$left . '{ '; foreach my $k (sort keys %$h) { $d .= "'$k' => "; # Compute left spaces in case we need to recurse $d =~ m/([^\n]*)$/s; my $indent = length($1); my $v = $h->{$k}; if (ref($v) eq 'ARRAY') { $d .= '[ ' . join("\n".' 'x$indent.', ', @$v); if (1 < scalar(@$v)) { $d .= "\n".' 'x$indent; } else { $d .= ' '; } $d .= ']'; } elsif (ref($v) eq 'HASH') { $d .= "\n"; $d .= dumphash ($v, $indent); } else { $d .= "'$v'"; } # Prepare for next key with initial left spaces and comma $d .= "\n" . ' 'x$left . ', '; } # Replace last comma with closing curly braces $d =~ s/, $/}\n/; return $d; } =head2 C<parmvalue ($parm, $pg)> Function C<parmvalue> dumps a parameter value if it exists in this parameter group. After testing for parameter existence, processing is dispatched according to type value. Parameter C<dbpass> is not dumped for security reason. =over =item 1 C<$parm> a parameter name as a I<string> =item 2 C<$pg> a reference to a parameter group =back =cut sub parmvalue { my $parm = shift; my $pg = shift; my $fallback = shift; return '' if !exists($pg->{$parm}) && !defined($fallback); my $val = $pg->{$parm} // $fallback->{$parm}; if (ref($val) eq 'HASH') { return '<pre>' . dumphash($val, 0) . '</pre>'; } elsif (ref($val) eq 'ARRAY') { return '<pre>' . join('<br>', @$val) . '</pre>'; } else { if ('dbpass' eq $parm) { return '<h4>Hey, that\'s supposed to be a secret!</h4>'; } else { return "<pre>$val</pre>"; } } } =head2 C<parmexpand ($templ, $who, $pgs, $pgnr)> Function C<parmexpand> is a "$function" substitution function. It returns its block (contained in C<$tmpl>) expanded for each accessible configuration parameter. =over =item 1 C<$templ> a I<string> containing the template (i.e. argument) =item 2 C<$who> a I<string> containing the script name (i.e. showconfig) requesting this substitution =item 3 C<$pgs> a reference to the parameter group array =item 4 C<$pgnr> parameter group index =back Parameter names are obtained from global C<$config> hash reference since all parameters end up there. "I<internal>" parameters C<'confpath'> and C<'parmgroupnr'> are removed from the set. A parameter may be defined in an included file like C<'filetype'> from C<'filetypeconf'>. In this case, both specific and global columns are empty. A parameter is considered if it belongs in the global section or in the requested tree section. To dump values anyway, force C<$config> usage instead of a parameter group through query argument C<_confall> with non zero value. =cut sub parmexpand { my ($templ, $who, $pgs, $pgnr) = @_; my $ret; my @keylist = (); my $parmgroup = @$pgs[$pgnr]; my $globgroup = @$pgs[0]; my $full = $HTTP->{'param'}{'_confall'} // 0; if ($full != 0) { my %seen; for (@$pgs) { while (defined(my $key = each %{$_})) { $seen{$key}++; } } if (1 < $full) { for (keys %$config) { $seen{$_}++ } } @keylist = keys(%seen); } else { @keylist = keys %{{%$parmgroup, %$globgroup}}; } for my $parm (sort @keylist) { next if ( $parm eq 'confpath' || $parm eq 'parmgroupnr' ); my $extra = !exists($$parmgroup{$parm}) && !exists($$globgroup{$parm}); $ret .= expandtemplate ( $templ , ( 'force' => sub{ $extra ? 'conf_force' : '' } , 'parm' => sub{ $parm } , 'type' => sub{ my $t = ref($config->{$parm}); if ('' ne $t) { return lc($t); } return 'string'; } , 'val' => sub{ parmvalue ( $parm , $parmgroup , ( 1 < $full ? $config : undef ) ) } , 'global'=> sub{ parmvalue($parm, $globgroup) } ) ); } return $ret; } =head2 C<parmgrouplink ($pgnr, $pgs)> Function C<parmgrouplink> is a "$variable" substitution function. It returns an C<E<lt> A E<gt>> element invoking script I<showconfig> to dump the designated parameter group. =over =item 1 C<$pgnr> parameter group index =item 2 C<$pgs> a reference to the parameter group array =back Link is created only if C<$pgnr> has an acceptable value. Otherwise, function returns string C<'none'>. =cut sub parmgrouplink { my ($pgnr, $pgs) = @_; if (0>=$pgnr || $pgnr > $#$pgs) { return 'none'; } else { return "#$pgnr <a href='" . $config->treeurl($$pgs[$pgnr], $$pgs[0]) . 'showconfig' . ( exists($$pgs[$pgnr]->{'treename'}) ? '/'.$$pgs[$pgnr]->{'treename'} : '' ) . "?_parmgroup=$pgnr'> " . ($$pgs[$pgnr]->{'virtroot'} // $$pgs[0]->{'virtroot'}) . (exists($$pgs[$pgnr]->{'treename'}) ? '/…/' . $$pgs[$pgnr]->{'treename'} : '' ) . '</a>' ; } } =head2 Script entry point Output is controlled by a template Eventually, a specific parameter group may be dumped by passing its index in URL argument C<_parmgroup>. This index may receive a default value through configuration parameter C<'parmgroupnr'>. =cut my $errorsig = "<!-- ! -->"; my $templ; httpinit(); std_http_headers(); my $who = 'showconfig'; my @pgs = $config->readconfig(); my $which = $HTTP->{'param'}{'_parmgroup'} // $config->{'parmgroupnr'} // 1; makeheader($who); $templ = gettemplate ( 'htmlconfig' , $errorsig ); if ($templ =~ m/^$errorsig/) { die "Can't display configuration without 'htmlconfig' template\n"; } print expandtemplate ( $templ , ( 'conffile' => sub { '<em>' . $config->{'confpath'} . '</em>' } , 'virtroot' => sub { $pgs[$which]->{'virtroot'} } , 'parmgroupnr' => sub { $which . (1 < $HTTP->{'param'}{'_confall'} ? ' (apocalyptical)' : '' ) } , 'varbtnaction'=> sub { varbtnaction(@_, $who) } , 'previous' => sub { parmgrouplink($which-1, \@pgs) } , 'next' => sub { parmgrouplink($which+1, \@pgs) } , 'conf_parm' => sub { parmexpand (@_, $who, \@pgs, $which) } ) ); makefooter($who); httpclean;