my @links;
foreach my $p (backlink_pages($page)) {
my $href=urlto($p, $page);
-
+
# Trim common dir prefixes from both pages.
my $p_trimmed=$p;
my $page_trimmed=$page;
$templatefile=$file;
}
});
- my $template=template(defined $templatefile ? $templatefile : 'page.tmpl', blind_cache => 1);
+ my $template;
+ if (defined $templatefile) {
+ $template=template_depends($templatefile, $page,
+ blind_cache => 1);
+ }
+ else {
+ # no explicit depends as special case
+ $template=template('page.tmpl',
+ blind_cache => 1);
+ }
my $actions=0;
if (length $config{cgiurl}) {
else {
$links{$page}=[];
}
+ delete $typedlinks{$page};
run_hooks(scan => sub {
shift->(
}
-sub verify_src_file ($$) {
- my $file=decode_utf8(shift);
- my $dir=shift;
-
- return if -l $file || -d _;
- $file=~s/^\Q$dir\E\/?//;
- return if ! length $file;
- my $page = pagename($file);
- if (! exists $pagesources{$page} &&
- file_pruned($file)) {
- $File::Find::prune=1;
- return;
- }
-
- my ($file_untainted) = $file =~ /$config{wiki_file_regexp}/; # untaint
- if (! defined $file_untainted) {
- warn(sprintf(gettext("skipping bad filename %s"), $file)."\n");
- }
- return ($file_untainted, $page);
-}
-
sub find_src_files () {
my @files;
my %pages;
eval q{use File::Find};
error($@) if $@;
- find({
- no_chdir => 1,
- wanted => sub {
- my ($file, $page) = verify_src_file($_, $config{srcdir});
- if (defined $file) {
- push @files, $file;
- if ($pages{$page}) {
- debug(sprintf(gettext("%s has multiple possible source pages"), $page));
+
+ my ($page, $dir, $underlay);
+ my $helper=sub {
+ my $file=decode_utf8($_);
+
+ return if -l $file || -d _;
+ $file=~s/^\Q$dir\E\/?//;
+ return if ! length $file;
+ $page = pagename($file);
+ if (! exists $pagesources{$page} &&
+ file_pruned($file)) {
+ $File::Find::prune=1;
+ return;
+ }
+
+ my ($f) = $file =~ /$config{wiki_file_regexp}/; # untaint
+ if (! defined $f) {
+ warn(sprintf(gettext("skipping bad filename %s"), $file)."\n");
+ }
+
+ if ($underlay) {
+ # avoid underlaydir override attacks; see security.mdwn
+ if (! -l "$config{srcdir}/$f" && ! -e _) {
+ if (! $pages{$page}) {
+ push @files, $f;
+ $pages{$page}=1;
}
- $pages{$page}=1;
}
- },
- }, $config{srcdir});
- foreach my $dir (@{$config{underlaydirs}}, $config{underlaydir}) {
+ }
+ else {
+ push @files, $f;
+ if ($pages{$page}) {
+ debug(sprintf(gettext("%s has multiple possible source pages"), $page));
+ }
+ $pages{$page}=1;
+ }
+ };
+
+ find({
+ no_chdir => 1,
+ wanted => $helper,
+ }, $dir=$config{srcdir});
+ $underlay=1;
+ foreach (@{$config{underlaydirs}}, $config{underlaydir}) {
find({
no_chdir => 1,
- wanted => sub {
- my ($file, $page) = verify_src_file($_, $dir);
- if (defined $file) {
- # avoid underlaydir override
- # attacks; see security.mdwn
- if (! -l "$config{srcdir}/$file" &&
- ! -e _) {
- if (! $pages{$page}) {
- push @files, $file;
- $pages{$page}=1;
- }
- }
- }
- },
- }, $dir);
+ wanted => $helper,
+ }, $dir=$_);
};
return \@files, \%pages;
}
my @new;
my @internal_new;
+ my $times_noted;
+
foreach my $file (@$files) {
my $page=pagename($file);
if (exists $pagesources{$page} && $pagesources{$page} ne $file) {
if (isinternal($page)) {
push @internal_new, $file;
}
- else {
+ elsif ($config{rcs}) {
push @new, $file;
- if ($config{getctime} && -e "$config{srcdir}/$file") {
+ if ($config{gettime} && -e "$config{srcdir}/$file") {
+ if (! $times_noted) {
+ debug(sprintf(gettext("querying %s for file creation and modification times.."), $config{rcs}));
+ $times_noted=1;
+ }
+
+ eval {
+ my $ctime=rcs_getctime("$config{srcdir}/$file");
+ if ($ctime > 0) {
+ $pagectime{$page}=$ctime;
+ }
+ };
+ if ($@) {
+ print STDERR $@;
+ }
+ my $mtime;
eval {
- my $time=rcs_getctime("$config{srcdir}/$file");
- $pagectime{$page}=$time;
+ $mtime=rcs_getmtime("$config{srcdir}/$file");
};
if ($@) {
print STDERR $@;
}
+ elsif ($mtime > 0) {
+ utime($mtime, $mtime, "$config{srcdir}/$file");
+ }
}
}
$pagecase{lc $page}=$page;
else {
push @del, $pagesources{$page};
}
- $dellinks{$page}= $links{$page};
$links{$page}=[];
- $delrenderedfiles{$page}= $renderedfiles{$page};
+ delete $typedlinks{$page};
$renderedfiles{$page}=[];
$pagemtime{$page}=0;
}
foreach my $file (@_) {
my $page=pagename($file);
if (! isinternal($page)) {
- debug(sprintf(gettext("removing old page %s"), $page));
+ debug(sprintf(gettext("removing obsolete %s"), $page));
}
foreach my $old (@{$oldrenderedfiles{$page}}) {
}
}
+sub link_types_changed ($$) {
+ # each is of the form { type => { link => 1 } }
+ my $new = shift;
+ my $old = shift;
+
+ return 0 if !defined $new && !defined $old;
+ return 1 if (!defined $new && %$old) || (!defined $old && %$new);
+
+ while (my ($type, $links) = each %$new) {
+ foreach my $link (keys %$links) {
+ return 1 unless exists $old->{$type}{$link};
+ }
+ }
+
+ while (my ($type, $links) = each %$old) {
+ foreach my $link (keys %$links) {
+ return 1 unless exists $new->{$type}{$link};
+ }
+ }
+
+ return 0;
+}
+
sub calculate_changed_links ($$$) {
my ($changed, $del, $oldlink_targets)=@_;
}
$linkchangers{lc($page)}=1;
}
+
+ # we currently assume that changing the type of a link doesn't
+ # change backlinks
+ if (!exists $linkchangers{lc($page)}) {
+ if (link_types_changed($typedlinks{$page}, $oldtypedlinks{$page})) {
+ $linkchangers{lc($page)}=1;
+ }
+ }
}
return \%backlinkchanged, \%linkchangers;
if (exists $depends{$p} && ! defined $reason) {
foreach my $dep (keys %{$depends{$p}}) {
my $sub=pagespec_translate($dep);
- next if $@ || ! defined $sub;
+ next unless defined $sub;
# only consider internal files
# if the page explicitly depends
if ($type == $IkiWiki::DEPEND_LINKS) {
next unless $linkchangers->{lc($page)};
}
- return $page;
+ $reason=$page;
+ return 1;
}
}
return undef;
};
if ($depends{$p}{$dep} & $IkiWiki::DEPEND_CONTENT) {
- last if $reason =
- $in->(\@changed, $IkiWiki::DEPEND_CONTENT);
- last if $internal_dep && ($reason =
+ last if $in->(\@changed, $IkiWiki::DEPEND_CONTENT);
+ last if $internal_dep && (
$in->($internal_new, $IkiWiki::DEPEND_CONTENT) ||
$in->($internal_del, $IkiWiki::DEPEND_CONTENT) ||
- $in->($internal_changed, $IkiWiki::DEPEND_CONTENT));
+ $in->($internal_changed, $IkiWiki::DEPEND_CONTENT)
+ );
}
if ($depends{$p}{$dep} & $IkiWiki::DEPEND_PRESENCE) {
- last if $reason =
- $in->(\@exists_changed, $IkiWiki::DEPEND_PRESENCE);
- last if $internal_dep && ($reason =
+ last if $in->(\@exists_changed, $IkiWiki::DEPEND_PRESENCE);
+ last if $internal_dep && (
$in->($internal_new, $IkiWiki::DEPEND_PRESENCE) ||
- $in->($internal_del, $IkiWiki::DEPEND_PRESENCE));
+ $in->($internal_del, $IkiWiki::DEPEND_PRESENCE)
+ );
}
if ($depends{$p}{$dep} & $IkiWiki::DEPEND_LINKS) {
- last if $reason =
- $in->(\@changed, $IkiWiki::DEPEND_LINKS);
- last if $internal_dep && ($reason =
+ last if $in->(\@changed, $IkiWiki::DEPEND_LINKS);
+ last if $internal_dep && (
$in->($internal_new, $IkiWiki::DEPEND_LINKS) ||
$in->($internal_del, $IkiWiki::DEPEND_LINKS) ||
- $in->($internal_changed, $IkiWiki::DEPEND_LINKS));
+ $in->($internal_changed, $IkiWiki::DEPEND_LINKS)
+ );
}
}
}
}
}
+sub gen_autofile ($$$) {
+ my $autofile=shift;
+ my $pages=shift;
+ my $del=shift;
+
+ if (file_pruned($autofile)) {
+ return;
+ }
+
+ my ($file)="$config{srcdir}/$autofile" =~ /$config{wiki_file_regexp}/; # untaint
+ if (! defined $file) {
+ return;
+ }
+
+ # Remember autofiles that were tried, and never try them again later.
+ if (exists $wikistate{$autofiles{$autofile}{plugin}}{autofile}{$autofile}) {
+ return;
+ }
+ $wikistate{$autofiles{$autofile}{plugin}}{autofile}{$autofile}=1;
+
+ if (srcfile($autofile, 1) || file_pruned($autofile)) {
+ return;
+ }
+
+ if (-l $file || -d _ || -e _) {
+ return;
+ }
+
+ my $page = pagename($file);
+ if ($pages->{$page}) {
+ return;
+ }
+
+ if (grep { $_ eq $autofile } @$del) {
+ return;
+ }
+
+ $autofiles{$autofile}{generator}->();
+ $pages->{$page}=1;
+ return 1;
+}
+
+
sub refresh () {
srcdir_check();
run_hooks(refresh => sub { shift->() });
scan($file);
}
- my %del_hash = map {$_, 1} @$del;
- while (my $autofile = shift (@autofiles)) {
- my $page=pagename($autofile);
- if (exists $del_hash{$page}) {
- $links{$page}= $dellinks{$page};
- $renderedfiles{$page}= $delrenderedfiles{$page};
- delete $del_hash{$page};
+ foreach my $autofile (keys %autofiles) {
+ if (gen_autofile($autofile, $pages, $del)) {
+ push @{$files}, $autofile;
+ push @{$new}, $autofile if find_new_files([$autofile]);
+ push @{$changed}, $autofile if find_changed([$autofile]);
+
+ scan($autofile);
}
- if ($pages->{$page}) {
- debug(sprintf(gettext("%s has multiple possible source pages"), $page));
- }
- $pages->{$page}=1;
-
- push @{$files}, $autofile;
- push @{$new}, $autofile if find_new_files([$autofile]);
- push @{$changed}, $autofile if find_changed([$autofile]);
-
- scan($autofile);
}
- $del = [keys %del_hash];
calculate_links();
foreach my $file (@$new, @$del) {
render_linkers($file);
}
-
- if (@$changed || @$internal_changed ||
+
+ if ($rendered{"templates/page.tmpl"}) {
+ foreach my $f (@$files) {
+ next if $f eq "templates/page.tmpl";
+ render($f, sprintf(gettext("building %s, which depends on %s"), $f, "templates/page.tmpl"));
+ }
+ }
+ elsif (@$changed || @$internal_changed ||
@$del || @$internal_del || @$internal_new) {
1 while render_dependent($files, $new, $internal_new,
$del, $internal_del, $internal_changed,
}
}
+sub clean_rendered {
+ lockwiki();
+ loadindex();
+ remove_unrendered();
+ foreach my $page (keys %oldrenderedfiles) {
+ foreach my $file (@{$oldrenderedfiles{$page}}) {
+ prune($config{destdir}."/".$file);
+ }
+ }
+}
+
sub commandline_render () {
lockwiki();
loadindex();