X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/b8d7ae91d0199003173ea3c581404d66538a3fba..eceea9db028729d68b319f8b4e95a4ca53a35c2b:/IkiWiki.pm
diff --git a/IkiWiki.pm b/IkiWiki.pm
index aea5b7abf..0af4a4fe9 100644
--- a/IkiWiki.pm
+++ b/IkiWiki.pm
@@ -33,13 +33,28 @@ sub defaultconfig () { #{{{
qr/\.x?html?$/, qr/\.ikiwiki-new$/,
qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//,
qr/\.dpkg-tmp$/],
- wiki_link_regexp => qr/\[\[(?:([^\]\|]+)\|)?([^\s\]#]+)(?:#([^\s\]]+))?\]\]/,
+ wiki_link_regexp => qr{
+ \[\[ # beginning of link
+ (?:
+ ([^\]\|]+) # 1: link text
+ \| # followed by '|'
+ )? # optional
+
+ ([^\s\]#]+) # 2: page to link to
+ (?:
+ \# # '#', beginning of anchor
+ ([^\s\]]+) # 3: anchor text
+ )? # optional
+
+ \]\] # end of link
+ }x,
wiki_file_regexp => qr/(^[-[:alnum:]_.:\/+]+$)/,
web_commit_regexp => qr/^web commit (by (.*?(?=: |$))|from (\d+\.\d+\.\d+\.\d+)):?(.*)/,
verbose => 0,
syslog => 0,
wikiname => "wiki",
default_pageext => "mdwn",
+ htmlext => "html",
cgi => 0,
post_commit => 0,
rcs => '',
@@ -71,6 +86,7 @@ sub defaultconfig () { #{{{
adminemail => undef,
plugin => [qw{mdwn inline htmlscrubber passwordauth openid signinedit
lockedit conditional}],
+ libdir => undef,
timeformat => '%c',
locale => undef,
sslcookie => 0,
@@ -112,7 +128,7 @@ sub checkconfig () { #{{{
unless exists $config{wikistatedir};
if ($config{rcs}) {
- eval qq{require IkiWiki::Rcs::$config{rcs}};
+ eval qq{use IkiWiki::Rcs::$config{rcs}};
if ($@) {
error("Failed to load RCS module IkiWiki::Rcs::$config{rcs}: $@");
}
@@ -125,8 +141,12 @@ sub checkconfig () { #{{{
} #}}}
sub loadplugins () { #{{{
+ if (defined $config{libdir}) {
+ unshift @INC, $config{libdir};
+ }
+
loadplugin($_) foreach @{$config{plugin}};
-
+
run_hooks(getopt => sub { shift->() });
if (grep /^-/, @ARGV) {
print STDERR "Unknown option: $_\n"
@@ -192,7 +212,7 @@ sub log_message ($$) { #{{{
sub possibly_foolish_untaint ($) { #{{{
my $tainted=shift;
- my ($untainted)=$tainted=~/(.*)/;
+ my ($untainted)=$tainted=~/(.*)/s;
return $untainted;
} #}}}
@@ -242,7 +262,7 @@ sub targetpage ($$) { #{{{
sub htmlpage ($) { #{{{
my $page=shift;
- return targetpage($page, "html");
+ return targetpage($page, $config{htmlext});
} #}}}
sub srcfile ($) { #{{{
@@ -452,7 +472,7 @@ sub displaytime ($) { #{{{
sub beautify_url ($) { #{{{
my $url=shift;
- $url =~ s!/index.html$!/!;
+ $url =~ s!/index.$config{htmlext}$!/!;
$url =~ s!^$!./!; # Browsers don't like empty links...
return $url;
@@ -526,7 +546,12 @@ sub htmllink ($$$;@) { #{{{
$bestlink.="#".$opts{anchor};
}
- return "$linktext";
+ my @attrs;
+ if (defined $opts{rel}) {
+ push @attrs, ' rel="'.$opts{rel}.'"';
+ }
+
+ return "$linktext";
} #}}}
sub htmlize ($$$) { #{{{
@@ -592,14 +617,24 @@ sub preprocess ($$$;$$) { #{{{
my $command=shift;
my $params=shift;
if (length $escape) {
- return "[[$command $params]]";
+ return "\\[[$command $params]]";
}
elsif (exists $hooks{preprocess}{$command}) {
return "" if $scan && ! $hooks{preprocess}{$command}{scan};
# Note: preserve order of params, some plugins may
# consider it significant.
my @params;
- while ($params =~ /(?:(\w+)=)?(?:"""(.*?)"""|"([^"]+)"|(\S+))(?:\s+|$)/sg) {
+ while ($params =~ m{
+ (?:(\w+)=)? # 1: named parameter key?
+ (?:
+ """(.*?)""" # 2: triple-quoted value
+ |
+ "([^"]+)" # 3: single-quoted value
+ |
+ (\S+) # 4: unquoted value
+ )
+ (?:\s+|$) # delimiter to next param
+ }sgx) {
my $key=$1;
my $val;
if (defined $2) {
@@ -643,20 +678,42 @@ sub preprocess ($$$;$$) { #{{{
return $ret;
}
else {
- return "[[$command $params]]";
+ return "\\[[$command $params]]";
}
};
- $content =~ s{(\\?)\[\[(\w+)\s+((?:(?:\w+=)?(?:""".*?"""|"[^"]+"|[^\s\]]+)\s*)*)\]\]}{$handle->($1, $2, $3)}seg;
+ $content =~ s{
+ (\\?) # 1: escape?
+ \[\[ # directive open
+ (\w+) # 2: command
+ \s+
+ ( # 3: the parameters..
+ (?:
+ (?:\w+=)? # named parameter key?
+ (?:
+ """.*?""" # triple-quoted value
+ |
+ "[^"]+" # single-quoted value
+ |
+ [^\s\]]+ # unquoted value
+ )
+ \s* # whitespace or end
+ # of directive
+ )
+ *) # 0 or more parameters
+ \]\] # directive closed
+ }{$handle->($1, $2, $3)}sexg;
return $content;
} #}}}
-sub filter ($$) { #{{{
+sub filter ($$$) { #{{{
my $page=shift;
+ my $destpage=shift;
my $content=shift;
run_hooks(filter => sub {
- $content=shift->(page => $page, content => $content);
+ $content=shift->(page => $page, destpage => $destpage,
+ content => $content);
});
return $content;
@@ -666,7 +723,8 @@ sub indexlink () { #{{{
return "$config{wikiname}";
} #}}}
-sub lockwiki () { #{{{
+sub lockwiki (;$) { #{{{
+ my $wait=@_ ? shift : 1;
# Take an exclusive lock on the wiki to prevent multiple concurrent
# run issues. The lock will be dropped on program exit.
if (! -d $config{wikistatedir}) {
@@ -675,15 +733,21 @@ sub lockwiki () { #{{{
open(WIKILOCK, ">$config{wikistatedir}/lockfile") ||
error ("cannot write to $config{wikistatedir}/lockfile: $!");
if (! flock(WIKILOCK, 2 | 4)) { # LOCK_EX | LOCK_NB
- debug("wiki seems to be locked, waiting for lock");
- my $wait=600; # arbitrary, but don't hang forever to
- # prevent process pileup
- for (1..$wait) {
- return if flock(WIKILOCK, 2 | 4);
- sleep 1;
+ if ($wait) {
+ debug("wiki seems to be locked, waiting for lock");
+ my $wait=600; # arbitrary, but don't hang forever to
+ # prevent process pileup
+ for (1..$wait) {
+ return if flock(WIKILOCK, 2 | 4);
+ sleep 1;
+ }
+ error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)");
+ }
+ else {
+ return 0;
}
- error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)");
}
+ return 1;
} #}}}
sub unlockwiki () { #{{{
@@ -737,9 +801,9 @@ sub loadindex () { #{{{
$depends{$page}=$items{depends}[0] if exists $items{depends};
$destsources{$_}=$page foreach @{$items{dest}};
$renderedfiles{$page}=[@{$items{dest}}];
- $oldrenderedfiles{$page}=[@{$items{dest}}];
$pagecase{lc $page}=$page;
}
+ $oldrenderedfiles{$page}=[@{$items{dest}}];
$pagectime{$page}=$items{ctime}[0];
}
close IN;
@@ -789,7 +853,6 @@ sub template_params (@) { #{{{
return "";
}
- require HTML::Template;
my @ret=(
filter => sub {
my $text_ref = shift;
@@ -804,6 +867,7 @@ sub template_params (@) { #{{{
} #}}}
sub template ($;@) { #{{{
+ require HTML::Template;
HTML::Template->new(template_params(@_));
} #}}}
@@ -968,7 +1032,21 @@ sub pagespec_translate ($) { #{{{
# Convert spec to perl code.
my $code="";
- while ($spec=~m/\s*(\!|\(|\)|\w+\([^\)]+\)|[^\s()]+)\s*/ig) {
+ while ($spec=~m{
+ \s* # ignore whitespace
+ ( # 1: match a single word
+ \! # !
+ |
+ \( # (
+ |
+ \) # )
+ |
+ \w+\([^\)]+\) # command(params)
+ |
+ [^\s()]+ # any other text
+ )
+ \s* # ignore whitespace
+ }igx) {
my $word=$1;
if (lc $word eq "and") {
$code.=" &&";
@@ -1047,8 +1125,8 @@ sub match_glob ($$;@) { #{{{
# relative matching
if ($glob =~ m!^\./!) {
- $from=~s!/?[^/]+$!!;
- $glob=~s!^\./!!;
+ $from=~s#/?[^/]+$##;
+ $glob=~s#^\./##;
$glob="$from/$glob" if length $from;
}
@@ -1074,18 +1152,23 @@ sub match_link ($$;@) { #{{{
# relative matching
if ($link =~ m!^\.! && defined $from) {
- $from=~s!/?[^/]+$!!;
- $link=~s!^\./!!;
+ $from=~s#/?[^/]+$##;
+ $link=~s#^\./##;
$link="$from/$link" if length $from;
}
my $links = $IkiWiki::links{$page} or return undef;
return IkiWiki::FailReason->new("$page has no links") unless @$links;
my $bestlink = IkiWiki::bestlink($from, $link);
- return IkiWiki::FailReason->new("no such link") unless length $bestlink;
foreach my $p (@$links) {
- return IkiWiki::SuccessReason->new("$page links to $link")
- if $bestlink eq IkiWiki::bestlink($page, $p);
+ if (length $bestlink) {
+ return IkiWiki::SuccessReason->new("$page links to $link")
+ if $bestlink eq IkiWiki::bestlink($page, $p);
+ }
+ else {
+ return IkiWiki::SuccessReason->new("$page links to page matching $link")
+ if match_glob($p, $link, %params);
+ }
}
return IkiWiki::FailReason->new("$page does not link to $link");
} #}}}