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 => '',
setup => undef,
adminuser => undef,
adminemail => undef,
- plugin => [qw{mdwn inline htmlscrubber passwordauth signinedit
+ plugin => [qw{mdwn inline htmlscrubber passwordauth openid signinedit
lockedit conditional}],
+ libdir => undef,
timeformat => '%c',
locale => undef,
sslcookie => 0,
httpauth => 0,
userdir => "",
- usedirs => 0,
+ usedirs => 1,
numbacklinks => 10,
+ account_creation_password => "",
} #}}}
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}: $@");
}
} #}}}
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"
$log_open=1;
}
eval {
- Sys::Syslog::syslog($type, "%s", join(" ", @_));
+ Sys::Syslog::syslog($type, "[$config{wikiname}] %s", join(" ", @_));
};
}
elsif (! $config{cgi}) {
sub possibly_foolish_untaint ($) { #{{{
my $tainted=shift;
- my ($untainted)=$tainted=~/(.*)/;
+ my ($untainted)=$tainted=~/(.*)/s;
return $untainted;
} #}}}
sub htmlpage ($) { #{{{
my $page=shift;
- return targetpage($page, "html");
+ return targetpage($page, $config{htmlext});
} #}}}
sub srcfile ($) { #{{{
}
} while $cwd=~s!/?[^/]+$!!;
- if (length $config{userdir} && exists $links{"$config{userdir}/".lc($link)}) {
- return "$config{userdir}/".lc($link);
+ if (length $config{userdir}) {
+ my $l = "$config{userdir}/".lc($link);
+ if (exists $links{$l}) {
+ return $l;
+ }
+ elsif (exists $pagecase{lc $l}) {
+ return $pagecase{lc $l};
+ }
}
#print STDERR "warning: page $page, broken link: $link\n";
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;
$bestlink.="#".$opts{anchor};
}
- return "<a href=\"$bestlink\">$linktext</a>";
+ my @attrs;
+ if (defined $opts{rel}) {
+ push @attrs, ' rel="'.$opts{rel}.'"';
+ }
+
+ return "<a href=\"$bestlink\"@attrs>$linktext</a>";
} #}}}
sub htmlize ($$$) { #{{{
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) {
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;
return "<a href=\"$config{url}\">$config{wikiname}</a>";
} #}}}
-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}) {
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 () { #{{{
$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;
return "";
}
- require HTML::Template;
my @ret=(
filter => sub {
my $text_ref = shift;
} #}}}
sub template ($;@) { #{{{
+ require HTML::Template;
HTML::Template->new(template_params(@_));
} #}}}
# 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.=" &&";
package IkiWiki::FailReason;
-use overload (
- '""' => sub { return ${$_[0]} },
- '0+' => sub { return 0 },
+use overload ( #{{{
+ '""' => sub { ${$_[0]} },
+ '0+' => sub { 0 },
'!' => sub { bless $_[0], 'IkiWiki::SuccessReason'},
fallback => 1,
-);
+); #}}}
-sub new {
+sub new { #{{{
bless \$_[1], $_[0];
-}
+} #}}}
package IkiWiki::SuccessReason;
-use overload (
- '""' => sub { return ${$_[0]} },
- '0+' => sub { return 1 },
+use overload ( #{{{
+ '""' => sub { ${$_[0]} },
+ '0+' => sub { 1 },
'!' => sub { bless $_[0], 'IkiWiki::FailReason'},
fallback => 1,
-);
+); #}}}
-sub new {
+sub new { #{{{
bless \$_[1], $_[0];
-}
+}; #}}}
package IkiWiki::PageSpec;
# relative matching
if ($glob =~ m!^\./!) {
- $from=~s!/?[^/]+$!!;
- $glob=~s!^\./!!;
+ $from=~s#/?[^/]+$##;
+ $glob=~s#^\./##;
$glob="$from/$glob" if length $from;
}
# 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");
} #}}}