+ # disable logging to syslog while dumping, broken plugins may
+ # whine when loaded
+ my $syslog=$config{syslog};
+ $config{syslog}=undef;
+
+ # Load all plugins, so that all setup options are available.
+ my %original_loaded_plugins=%IkiWiki::loaded_plugins;
+ my @plugins=IkiWiki::listplugins();
+ foreach my $plugin (@plugins) {
+ eval { IkiWiki::loadplugin($plugin, 1) };
+ if (exists $IkiWiki::hooks{checkconfig}{$plugin}{call}) {
+ my @s=eval { $IkiWiki::hooks{checkconfig}{$plugin}{call}->() };
+ }
+ }
+ %IkiWiki::loaded_plugins=%original_loaded_plugins;
+
+ my %sections;
+ foreach my $plugin (@plugins) {
+ if (exists $IkiWiki::hooks{getsetup}{$plugin}{call}) {
+ # use an array rather than a hash, to preserve order
+ my @s=eval { $IkiWiki::hooks{getsetup}{$plugin}{call}->() };
+ next unless @s;
+
+ if (scalar(@s) % 2 != 0) {
+ print STDERR "warning: plugin $plugin has a broken getsetup; ignoring\n";
+ next;
+ }
+
+ # set default section value (note use of shared
+ # hashref between array and hash)
+ my %s=@s;
+ if (! exists $s{plugin} || ! $s{plugin}->{section}) {
+ $s{plugin}->{section}="other";
+ }
+
+ # only the selected rcs plugin is included
+ if ($config{rcs} && $plugin eq $config{rcs}) {
+ $s{plugin}->{section}="core";
+ }
+ elsif ($s{plugin}->{section} eq "rcs") {
+ next;
+ }
+
+ push @{$sections{$s{plugin}->{section}}}, [ $plugin, \@s ];
+ }
+ }
+
+ $config{syslog}=$syslog;
+
+ return map { sort { $a->[0] cmp $b->[0] } @{$sections{$_}} }
+ sort { # core first, other last, otherwise alphabetical
+ ($b eq "core") <=> ($a eq "core")
+ ||
+ ($a eq "other") <=> ($b eq "other")
+ ||
+ $a cmp $b
+ } keys %sections;
+}
+
+sub commented_dump ($$) {
+ my $dumpline=shift;
+ my $indent=shift;
+
+ my %setup=(%config);
+ my @ret;
+
+ # disable logging to syslog while dumping
+ $config{syslog}=undef;
+
+ eval q{use Text::Wrap};
+ die $@ if $@;
+
+ my %section_plugins;
+ push @ret, commented_dumpvalues($dumpline, $indent, \%setup, IkiWiki::getsetup());
+ foreach my $pair (IkiWiki::Setup::getsetup()) {
+ my $plugin=$pair->[0];
+ my $setup=$pair->[1];
+ my %s=@{$setup};
+ my $section=$s{plugin}->{section};
+ if (! defined $section) {
+ print STDERR "warning: missing section in $plugin\n";
+ $section="other";
+ }
+ push @{$section_plugins{$section}}, $plugin;
+ if (@{$section_plugins{$section}} == 1) {
+ push @ret, "", $indent.("#" x 70), "$indent# $section plugins",
+ sub {
+ wrap("$indent# (", "$indent# ",
+ join(", ", @{$section_plugins{$section}})).")"
+ },
+ $indent.("#" x 70);
+ }
+
+ my @values=commented_dumpvalues($dumpline, $indent, \%setup, @{$setup});
+ if (@values) {
+ push @ret, "", "$indent# $plugin plugin", @values;
+ }
+ }
+
+ return map { ref $_ ? $_->() : $_ } @ret;
+}
+
+sub commented_dumpvalues ($$$@) {
+ my $dumpline=shift;
+ my $indent=shift;
+ my $setup=shift;
+ my @ret;
+ while (@_) {
+ my $key=shift;
+ my %info=%{shift()};
+
+ next if $key eq "plugin" || $info{type} eq "internal";
+
+ push @ret, "$indent# ".$info{description} if exists $info{description};
+
+ if (exists $setup->{$key} && defined $setup->{$key}) {
+ push @ret, $dumpline->($key, $setup->{$key}, $info{type}, "");
+ delete $setup->{$key};
+ }
+ elsif (exists $info{example}) {
+ push @ret, $dumpline->($key, $info{example}, $info{type}, "#");
+ }
+ else {
+ push @ret, $dumpline->($key, "", $info{type}, "#");
+ }
+ }
+ return @ret;
+}