X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/blobdiff_plain/830c9e59b2c4d5c90e4316d8e81558e1aeb132f2..54fb82a5a47078f6865ed5bf7d7c09db4bf34e22:/IkiWiki.pm diff --git a/IkiWiki.pm b/IkiWiki.pm index b47da966e..9c386e154 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -13,8 +13,8 @@ use open qw{:utf8 :std}; use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase %pagestate %wikistate %renderedfiles %oldrenderedfiles - %pagesources %destsources %depends %hooks %forcerebuild - %loaded_plugins}; + %pagesources %destsources %depends %depends_simple %hooks + %forcerebuild %loaded_plugins}; use Exporter q{import}; our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match @@ -28,11 +28,17 @@ our $VERSION = 3.00; # plugin interface version, next is ikiwiki version our $version='unknown'; # VERSION_AUTOREPLACE done by Makefile, DNE our $installdir='/usr'; # INSTALLDIR_AUTOREPLACE done by Makefile, DNE +# Page dependency types. +our $DEPEND_CONTENT=1; +our $DEPEND_PRESENCE=2; +our $DEPEND_LINKS=4; + # Optimisation. use Memoize; memoize("abs2rel"); memoize("pagespec_translate"); memoize("file_pruned"); +memoize("template_file"); sub getsetup () { wikiname => { @@ -149,6 +155,13 @@ sub getsetup () { safe => 0, # path rebuild => 1, }, + templatedirs => { + type => "internal", + default => [], + description => "additional directories containing template files", + safe => 0, + rebuild => 0, + }, underlaydir => { type => "string", default => "$installdir/share/ikiwiki/basewiki", @@ -336,7 +349,7 @@ sub getsetup () { qr/\.x?html?$/, qr/\.ikiwiki-new$/, qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//, qr/(^|\/)_MTN\//, qr/(^|\/)_darcs\//, - qr/\.dpkg-tmp$/], + qr/(^|\/)CVS\//, qr/\.dpkg-tmp$/], description => "regexps of source files to ignore", safe => 0, rebuild => 1, @@ -356,7 +369,7 @@ sub getsetup () { }, web_commit_regexp => { type => "internal", - default => qr/^web commit (by (.*?(?=: |$))|from (\d+\.\d+\.\d+\.\d+)):?(.*)/, + default => qr/^web commit (by (.*?(?=: |$))|from ([0-9a-fA-F:.]+[0-9a-fA-F])):?(.*)/, description => "regexp to parse web commits from logs", safe => 0, rebuild => 0, @@ -536,12 +549,12 @@ sub loadplugins () { } if ($config{rcs}) { - if (exists $IkiWiki::hooks{rcs}) { + if (exists $hooks{rcs}) { error(gettext("cannot use multiple rcs plugins")); } loadplugin($config{rcs}); } - if (! exists $IkiWiki::hooks{rcs}) { + if (! exists $hooks{rcs}) { loadplugin("norcs"); } @@ -661,9 +674,15 @@ sub pagetype ($) { return; } +my %pagename_cache; + sub pagename ($) { my $file=shift; + if (exists $pagename_cache{$file}) { + return $pagename_cache{$file}; + } + my $type=pagetype($file); my $page=$file; $page=~s/\Q.$type\E*$// @@ -672,6 +691,8 @@ sub pagename ($) { if ($config{indexpages} && $page=~/(.*)\/index$/) { $page=$1; } + + $pagename_cache{$file} = $page; return $page; } @@ -1255,9 +1276,10 @@ sub preprocess ($$$;$$) { ); }; if ($@) { - chomp $@; + my $error=$@; + chomp $error; $ret="[[!$command ". - gettext("Error").": $@"."]]"; + gettext("Error").": $error"."]]"; } } else { @@ -1466,7 +1488,8 @@ sub loadindex () { %oldrenderedfiles=%pagectime=(); if (! $config{rebuild}) { %pagesources=%pagemtime=%oldlinks=%links=%depends= - %destsources=%renderedfiles=%pagecase=%pagestate=(); + %destsources=%renderedfiles=%pagecase=%pagestate= + %depends_simple=(); } my $in; if (! open ($in, "<", "$config{wikistatedir}/indexdb")) { @@ -1506,7 +1529,27 @@ sub loadindex () { $links{$page}=$d->{links}; $oldlinks{$page}=[@{$d->{links}}]; } - if (exists $d->{depends}) { + if (ref $d->{depends_simple} eq 'ARRAY') { + # old format + $depends_simple{$page}={ + map { $_ => 1 } @{$d->{depends_simple}} + }; + } + elsif (exists $d->{depends_simple}) { + $depends_simple{$page}=$d->{depends_simple}; + } + if (exists $d->{dependslist}) { + # old format + $depends{$page}={ + map { $_ => $DEPEND_CONTENT } + @{$d->{dependslist}} + }; + } + elsif (exists $d->{depends} && ! ref $d->{depends}) { + # old format + $depends{$page}={$d->{depends} => $DEPEND_CONTENT }; + } + elsif (exists $d->{depends}) { $depends{$page}=$d->{depends}; } if (exists $d->{state}) { @@ -1556,6 +1599,10 @@ sub saveindex () { $index{page}{$src}{depends} = $depends{$page}; } + if (exists $depends_simple{$page}) { + $index{page}{$src}{depends_simple} = $depends_simple{$page}; + } + if (exists $pagestate{$page}) { foreach my $id (@hookids) { foreach my $key (keys %{$pagestate{$page}{$id}}) { @@ -1585,7 +1632,8 @@ sub saveindex () { sub template_file ($) { my $template=shift; - foreach my $dir ($config{templatedir}, "$installdir/share/ikiwiki/templates") { + foreach my $dir ($config{templatedir}, @{$config{templatedirs}}, + "$installdir/share/ikiwiki/templates") { return "$dir/$template" if -e "$dir/$template"; } return; @@ -1720,19 +1768,58 @@ sub rcs_receive () { $hooks{rcs}{rcs_receive}{call}->(); } -sub add_depends ($$) { +sub add_depends ($$;@) { my $page=shift; my $pagespec=shift; - - return unless pagespec_valid($pagespec); - if (! exists $depends{$page}) { - $depends{$page}=$pagespec; + # Is the pagespec a simple page name? + my $simple=$pagespec =~ /$config{wiki_file_regexp}/ && + $pagespec !~ /[\s*?()!]/; + + my $deptype=0; + if (@_) { + my %params=@_; + + if ($params{presence}) { + # Is the pagespec limited to terms that will continue + # to match pages as long as those pages exist? + my $presence_limited=1; + while ($presence_limited && $pagespec=~m/(\w+)\([^\)]*\)/g) { + $presence_limited = $1 =~ /^(glob|internal|creation_month|creation_day|creation_year|created_before|created_after)$/; + } + if ($presence_limited) { + $deptype=$deptype | $DEPEND_PRESENCE; + } + else { + $deptype=$deptype | $DEPEND_CONTENT; + } + } + if ($params{links}) { + # Is the pagespec limited to terms that will continue + # to match pages as long as those pages exist and + # link to the same places? + my $links_limited=1; + while ($links_limited && $pagespec=~m/(\w+)\([^\)]*\)/g) { + $links_limited = $1 =~ /^(glob|internal|creation_month|creation_day|creation_year|created_before|created_after|backlink)$/; + } + if ($links_limited) { + $deptype=$deptype | $DEPEND_LINKS; + } + else { + $deptype=$deptype | $DEPEND_CONTENT; + } + } } - else { - $depends{$page}=pagespec_merge($depends{$page}, $pagespec); + $deptype=$DEPEND_CONTENT unless $deptype; + + if ($simple) { + $depends_simple{$page}{lc $pagespec} |= $deptype; + return 1; } + return unless pagespec_valid($pagespec); + + $depends{$page}{$pagespec} |= $deptype; return 1; } @@ -1816,14 +1903,6 @@ sub add_link ($$) { unless grep { $_ eq $link } @{$links{$page}}; } -sub pagespec_merge ($$) { - my $a=shift; - my $b=shift; - - return $a if $a eq $b; - return "($a) or ($b)"; -} - sub pagespec_translate ($) { my $spec=shift; @@ -1844,13 +1923,13 @@ sub pagespec_translate ($) { [^\s()]+ # any other text ) \s* # ignore whitespace - }igx) { + }gx) { my $word=$1; if (lc $word eq 'and') { - $code.=' &&'; + $code.=' &'; } elsif (lc $word eq 'or') { - $code.=' ||'; + $code.=' |'; } elsif ($word eq "(" || $word eq ")" || $word eq "!") { $code.=' '.$word; @@ -1936,36 +2015,40 @@ sub glob2re ($) { package IkiWiki::FailReason; use overload ( - '""' => sub { ${$_[0]} }, + '""' => sub { $_[0][0] }, '0+' => sub { 0 }, '!' => sub { bless $_[0], 'IkiWiki::SuccessReason'}, + '&' => sub { $_[0][1]={%{$_[0][1]}, %{$_[1][1]}}; $_[0] }, + '|' => sub { $_[1][1]={%{$_[0][1]}, %{$_[1][1]}}; $_[1] }, fallback => 1, ); -sub new { - my $class = shift; - my $value = shift; - return bless \$value, $class; -} - -package IkiWiki::ErrorReason; - -our @ISA = 'IkiWiki::FailReason'; +our @ISA = 'IkiWiki::SuccessReason'; package IkiWiki::SuccessReason; use overload ( - '""' => sub { ${$_[0]} }, + '""' => sub { $_[0][0] }, '0+' => sub { 1 }, '!' => sub { bless $_[0], 'IkiWiki::FailReason'}, + '&' => sub { $_[1][1]={%{$_[0][1]}, %{$_[1][1]}}; $_[1] }, + '|' => sub { $_[0][1]={%{$_[0][1]}, %{$_[1][1]}}; $_[0] }, fallback => 1, ); sub new { my $class = shift; my $value = shift; - return bless \$value, $class; -}; + return bless [$value, {map { $_ => 1 } @_}], $class; +} + +sub influences { + return keys %{$_[0][1]}; +} + +package IkiWiki::ErrorReason; + +our @ISA = 'IkiWiki::FailReason'; package IkiWiki::PageSpec; @@ -2016,23 +2099,23 @@ sub match_link ($$;@) { my $from=exists $params{location} ? $params{location} : ''; my $links = $IkiWiki::links{$page}; - return IkiWiki::FailReason->new("$page has no links") unless $links && @{$links}; + return IkiWiki::FailReason->new("$page has no links", $link) unless $links && @{$links}; my $bestlink = IkiWiki::bestlink($from, $link); foreach my $p (@{$links}) { if (length $bestlink) { - return IkiWiki::SuccessReason->new("$page links to $link") + return IkiWiki::SuccessReason->new("$page links to $link", $page) if $bestlink eq IkiWiki::bestlink($page, $p); } else { - return IkiWiki::SuccessReason->new("$page links to page $p matching $link") + return IkiWiki::SuccessReason->new("$page links to page $p matching $link", $page) if match_glob($p, $link, %params); - $p=~s/^\///; + my ($p_rel)=$p=~/^\/?(.*)/; $link=~s/^\///; - return IkiWiki::SuccessReason->new("$page links to page $p matching $link") - if match_glob($p, $link, %params); + return IkiWiki::SuccessReason->new("$page links to page $p_rel matching $link", $page) + if match_glob($p_rel, $link, %params); } } - return IkiWiki::FailReason->new("$page does not link to $link"); + return IkiWiki::FailReason->new("$page does not link to $link", $page); } sub match_backlink ($$;@) { @@ -2048,14 +2131,14 @@ sub match_created_before ($$;@) { if (exists $IkiWiki::pagectime{$testpage}) { if ($IkiWiki::pagectime{$page} < $IkiWiki::pagectime{$testpage}) { - return IkiWiki::SuccessReason->new("$page created before $testpage"); + return IkiWiki::SuccessReason->new("$page created before $testpage", $testpage); } else { - return IkiWiki::FailReason->new("$page not created before $testpage"); + return IkiWiki::FailReason->new("$page not created before $testpage", $testpage); } } else { - return IkiWiki::FailReason->new("$testpage has no ctime"); + return IkiWiki::ErrorReason->new("$testpage does not exist", $testpage); } } @@ -2068,14 +2151,14 @@ sub match_created_after ($$;@) { if (exists $IkiWiki::pagectime{$testpage}) { if ($IkiWiki::pagectime{$page} > $IkiWiki::pagectime{$testpage}) { - return IkiWiki::SuccessReason->new("$page created after $testpage"); + return IkiWiki::SuccessReason->new("$page created after $testpage", $testpage); } else { - return IkiWiki::FailReason->new("$page not created after $testpage"); + return IkiWiki::FailReason->new("$page not created after $testpage", $testpage); } } else { - return IkiWiki::FailReason->new("$testpage has no ctime"); + return IkiWiki::ErrorReason->new("$testpage does not exist", $testpage); } }